{"_id":"plumier","_rev":"655-8cf633675572763eddd731ee1682e089","name":"plumier","dist-tags":{"canary":"1.1.3-canary.3","latest":"1.1.3"},"versions":{"0.1.0-alpha.d43b984e":{"name":"plumier","version":"0.1.0-alpha.d43b984e","description":"A testing friendly Web API framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"ISC","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","tslib":"^1.9.2"},"peerDependencies":{"reflect-metadata":"^0.1.12"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","supertest":"^3.1.0","typescript":"^2.9.2"},"_id":"plumier@0.1.0-alpha.d43b984e","_npmVersion":"5.2.0","_nodeVersion":"8.7.0","_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"dist":{"integrity":"sha512-G6LPTCzqanaf/2bCL2+g9TfLTmqutqkmTb1TQm9tAl1yF5X8SZTAZzUBL3lzqg4/gu5X0wHY0WRad1TbA5U6wA==","shasum":"679165d24dcf0bdd01d20309560e3715cb2a77f6","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.d43b984e.tgz","fileCount":13,"unpackedSize":57405,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPLFnCRA9TVsSAnZWagAAUDYP/1NyduikZ3wRd0niLfNx\nxxMZHmrESYjH+TXV+2M6TLDwohbGcYfir1Ddp0hb8nmzGi0TV0JsgIgyTpXW\n0jOoxOvy+kL2eSq2PqQBfR7a0HAdasOqrN39PxiFp2yxPtmQZn5nYMFSGglK\nkPHl+b/c0Fl/w3KsIbcXq1diuGJiezFHyYOPn8uR6STiERFHQJCEivYfZKjI\n2HAwquk+Y7PmJU0XPTJm9CiEFJsjfKH2JvsicV3PVeN9emFObI/RC2OnQfj/\nUT/dwcqMBCyHHytbBGkhj5AoV19WmcBGUNxQB9maI2Oz8IRSTzU1FKAwvVhF\ntW4s5HhDimZEBnE6oTxaqV2+eUzDO6byu+3d48Bd30JmLz0tD5ezk0VdmbEy\n4G0OJQ1Uyr0Ji90Z7AQIwX3iZeszzOndIny9lI/DQ1LNJ1l9uIJuC15BuM9L\nBXEmb6dWZuXFk+phBfTHtc2s4Ng4/xrATz9se/ofqw1kL1haCffaoaVy3RFn\nNsNqhSL6bN0HEzPYAJAB4Iyq9aOUL7LLyJkSnmHTRtzkyrcpWP27beZOQg6p\nqzcZ7nWHIk7kBDhcsJIdVLaeH0h2Ug6Y2ej4HG5xJ8S5pXDBKepuc8t0n+Ym\n1uYZvUkpDhVYcCcGYAz+nDDDrvKlo47UduWHRXOZjuvywA2BN3+YUMr/TaUV\nvVEj\r\n=yiiv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDIBJ2g7fjMonWWktXK3/fmNtjdzlxrri8AmNdeDRlY9wIhAIKODD/Cp+kKFGG6wFMURtpJ7Twa8yaqO5Jdmcf2A4hT"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.d43b984e_1530704231502_0.7591708114612248"},"_hasShrinkwrap":false},"0.1.0-alpha.606deb1d":{"name":"plumier","version":"0.1.0-alpha.606deb1d","description":"A testing friendly Web API framework","main":"index.js","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","reflect-metadata":"^0.1.12","tslib":"^1.9.2"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","supertest":"^3.1.0"},"readme":"ERROR: No README data found!","_id":"plumier@0.1.0-alpha.606deb1d","_npmVersion":"5.2.0","_nodeVersion":"8.7.0","_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"dist":{"integrity":"sha512-/rb5QneGoQIPF3cp4BFw/ZNjuQuVqB0ag/HibgWn+J0Re/513wlNg7Hrrq9wNEAg407MNsn4hN+/VcqXK0XLWg==","shasum":"e82b2e969c0d8a2018e5e25b88042ff8e04f8c21","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.606deb1d.tgz","fileCount":1,"unpackedSize":844,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPZKJCRA9TVsSAnZWagAAeysQAJvAZZY8YPeKagmH0n8Y\nO41DeMMvQonm/BMd+iQbIzp9dhY2Of3x1g7kOpBjPx1oTpPMMzZqA4BpvoWV\n4Y6Pm9GWdmWjzIvK7sRbVOyzV2fZhQVeMi2ddK4jUaVomJZDV4nbIT7fZs67\ncG0iYvBKSjO7f0gNIuNAuuATgNdm43nTSr9o0OR6NehbFm8IOyNK+0TTf+Ez\nI24qKbf6f0Hvemw9izUNf01fmciFoI2Ocxnb1bIVckK4jrK1f2ObL1ifyv6I\nOV6dLtfAORwzPHvBeRqBaKsNsB34ZyqqJHiBjr9T5WUj1cNe2f3uEIY2Lynv\nWAbqqdU7gF7QZLryyQ+Pu5/KIpaotp8sh0mefoJ3WrpUMIHw2RAWMi3eqpEF\niIWWGhXDQBo/Jd1Joh3WP2XgsuVUSyHDupXwAFP1XI46jNwVHRJWmxKqszXk\n44fd0+bnc3yb7NNgspsXH8ORhKmZGXrPjm7xQoF05WnS2isZfnumlMWr/7pQ\n4l8BglXSB63QyMIaczzijbIdaJp8WrBMurX2WclVOS5dhWfue9pfyAdZu4Y/\nBpXFDzXeElFKUSRHz9rmnOvU+8P0dsRcXmfEJk6kkh2M+aIb8n9ihhuG//TD\nGxTXH9Y7k28CxO3abKMuJxa/r6XG+AUFncoNPO+MBrBoigwS1O22tW6TH8re\nS2df\r\n=5mqw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDYw+jT6Bmnos0d+9EzlFzGp/ONeSQ2UEyO/1oaPlrFgQIgSY0XC85f00koTKo00HaVWkImtcrmqPldyYg+4pCdlaQ="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.606deb1d_1530761865149_0.8920316274336706"},"_hasShrinkwrap":false},"0.1.0-alpha.8f9a56cf":{"name":"plumier","version":"0.1.0-alpha.8f9a56cf","description":"A testing friendly Web API framework","main":"index.js","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","reflect-metadata":"^0.1.12","tslib":"^1.9.2"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","supertest":"^3.1.0"},"readme":"ERROR: No README data found!","_id":"plumier@0.1.0-alpha.8f9a56cf","_npmVersion":"5.2.0","_nodeVersion":"8.7.0","_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"dist":{"integrity":"sha512-npcprNdxenZRH6RfGHUA4uI0ksAadzEUj+QJRNuvZp5HFSDylrf9BqJsxpNM3DQzUmyik3XT40XhsbGOZgyO+A==","shasum":"6cf4716eba3850fa4523a1f313d53a2f399a5d66","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.8f9a56cf.tgz","fileCount":13,"unpackedSize":59548,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPZR/CRA9TVsSAnZWagAAXKcP/0Ki8C7ZbolMaVMg2rts\n8aodg92xpfcNq0tu28CiIcQFN4WEo3KDAgA/y3cul0WydG7haw9IXxwL/Wy3\nigNz/m7IfCpqeW13syvR0diVe34rIYJmKbbhD/rb3nU2OQtOQb4cTyDpSWO7\nYfo3l97kRPLpvlvmgdUO3aWGtmcN0SC9kmZFKuPbNg6dO7M4jBMieo6bt8vY\ndYdMDMcZmpJT00xoe6ojwGJbZDLM/9EOcYcDF65B0Xxh3HyD4ZomzYkOvdlY\nXjiE1oL2mUSShmqtZBfGs3v49DF3vq+IyTM3VnH54sXTPlxkSpbJTFKqLw+D\nh/6uai6IFXg+Xi22UANDEHXWHxwNC+vYirm3Gv5kkBtPpMnG3UFFR669/aE4\nDqaJyZBStZ9BSV6skolT2t+jWcQgS1t40Pyv84Ol3LHl/4lybUnBhKN0G1GP\nNVcxTioT8YHIERjIr8/SY1cTZQLyJJLj4lXkAurHhkcWM/sf5K6PfMR7+q2O\ncGUjBD0G/iCKiXQcO0I2+di3yrjR0T6l/aSe3bpgEC7qw9+ZOFhUwJ/jTaj7\ntKEZWQZJFXFBhaPRifPB6KCnuayt9/NP07tNUEOKdxH5zVNbxx0ZAD3CFelX\ncJRvuBJcmsK6w3ofHc9Xe7tb5LF6lN4hySEA7iIgZe2f7zdz4B6wy4jUIXhq\nHGB1\r\n=WZFg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCftNnbUTf7PYfz/veY+uGJjpBu8mPNmJNNNGAEK0na/gIhAPntcZo1H5lO1s6HX7gyj+8OqnbdEe2RN923Cd9eTRpP"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.8f9a56cf_1530762367474_0.27938094901100397"},"_hasShrinkwrap":false},"0.1.0-alpha.7335c00b":{"name":"plumier","version":"0.1.0-alpha.7335c00b","description":"A testing friendly Web API framework","main":"index.js","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","reflect-metadata":"^0.1.12","tslib":"^1.9.2"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","supertest":"^3.1.0"},"readme":"ERROR: No README data found!","_id":"plumier@0.1.0-alpha.7335c00b","_npmVersion":"5.2.0","_nodeVersion":"8.7.0","_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"dist":{"integrity":"sha512-EGzkR29+AT5vhOenLsyoGhpWw+7opVCS00EdPJY0zDMQ1n7tKFi+F/Z8ts6L+iS7dxTUwzOsU7y0TJsOo+BjhQ==","shasum":"07191299f109a267b2a865a73bd792bb9d5c4475","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.7335c00b.tgz","fileCount":13,"unpackedSize":62277,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPodrCRA9TVsSAnZWagAAeFMQAKG3SJL8P9NUxRlP7aI7\n91sRT6ZX5DOGtUWlM6Jdl/8uRDGhndJP9+wZRBhKFV/erJzZGS928W7Eh+wA\n+vUNJVk1WK7BaesSY8TcsXRlgzAfxnwNgknDiqJVOx264rKk+sVEhvTKPuup\noZQ9QxUf/1vwwW98TqzG/JJswsWgJzvTg+sIcKxjVxinT+GFFKROhBGgJX21\nDjMd0vMkljMY2pjsDsxJsTHAp++Daey/1rPRNKGgDVVvyAKEmtsZN0A0kUyY\n0Hr9Os/NgWbmdq75D+lSUxpn6GyPgKbulsDNoiBms3ypCw/l6Zp2HL6I95sB\nnnNm6IWterx8w66KjIGoK3urfRYavYUpakqn65uZUK2XGwMKkldVEbaNjcOt\nxDxFO1YjUbC5G1c4ooizoez0ZxUhaNd+60e6uumAy1TiyOk434v8honUlW92\n6Q7SnU3SehwetHGU+D+1dcS8rfCB/7l9AVyYBB1lKpocouu3uVdQ3sG9+rLK\nwmerofb1i8wLwqQYnnYTfGmoL0IeJ5t0ENRJEl1upyF9Fa8mFfvn1K7u0qgw\nS8b5bSXIhvd5vpQ63PtPwELv1UvmIk3xGwnu4NGIOXcUtcW0N+iV60T4mvlA\nlH6bA83pn82ZA05EQ1ULPCLEzbWo20HrmVUe8yPPDd7HOwmCuDrkA/cCm4oV\nufyo\r\n=kcug\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBHxZmTFXLqtsSHbEAIfA2kwEghhah+xYTn9VSoUya4YAiEAweANVzMWg+Ne6RZNCxhcI10IYpNKRIdMop3mAycsaO8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.7335c00b_1530824555453_0.7552707673428707"},"_hasShrinkwrap":false},"0.1.0-alpha.ad52811b":{"name":"plumier","version":"0.1.0-alpha.ad52811b","description":"A testing friendly Web API framework","main":"index.js","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","reflect-metadata":"^0.1.12","tslib":"^1.9.2"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","supertest":"^3.1.0"},"readme":"ERROR: No README data found!","_id":"plumier@0.1.0-alpha.ad52811b","_npmVersion":"5.2.0","_nodeVersion":"8.7.0","_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"dist":{"integrity":"sha512-utWxltDGuhs52twGs5qr1mcOoqgFcurjrTtsS+zPTNt9vjAZ5wxtrOmi1qxMqz3si6+T69KUnPOWMtsNjehNTA==","shasum":"c3e0eb14fd782010656b90473793359bf7161ab7","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.ad52811b.tgz","fileCount":13,"unpackedSize":65353,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPzZwCRA9TVsSAnZWagAAUVAP/1sHnopZeoDOOioNO8rQ\nXoC1abXGImbv1bcjxWm3B8zcnTLFvCsUkFG2uPyQ2o62D+DcPf0JDMUUJ5ck\nxnwGOn0ADyypZylAWsjphl1gcos3Aiq3nIUxJbYKllLkqCsRpFhohKoITVJ7\nYWts7t4pSCRR3+q1Ys3/cJ268rfE5fS+Z93fUHLN7G5V7fBapvf/OM64L0hr\nMcnaW8LGMGfnkc6Q6LoBQdTERnbfgBryRTyJ9P0qeDIYmvmPLX+JN89gjYQw\nWimOYjGn8HuXJu+qlxoNzTy05+3Vmva2mc37auwPAyS50nXdO2Ni6Srevk9f\nTG8EJ4/hx13JM8leMz1Lx6IuVkAgoiThD2JfgjNzzPXqnvshYpPux3GxWsYR\n04DJfWwiI7HProPdmXevD/IH46LFzDneqHwUf6KwoVzAiFgg0Zet7HdxiEwG\nXaFLdk20PJOl5kvLHVP8g8Xcg3SgBgHOymichS2hyA8zXv35kqZXv/yq/dEZ\nRUTeRr27R6lNJAHkDnXk1mNOq9UHY4O1/HtudwgCqTtJ0J9yrP2iujpY+7PR\nJZl7aaFyS3TBsoLFPlnZcmlsHSE5/MKelvNnXzUmDtPVBzfvgb5re1tUPjPX\nPpupXM25XNLMegpwDgn/HAYET8mkhfjayGC9PDjqKGjpU7QDFFtumvyCzhMz\nlugI\r\n=KLg7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC1BOxvHvz+4QfRrBH9uzmQMxl83YrYucm5952OmGwTgQIgWnORrsRaxY+27M3v2U/JzugcZ/gLKCoZOnvU6UhQZJ4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.ad52811b_1530869360151_0.6346774641532409"},"_hasShrinkwrap":false},"0.1.0-alpha.068e9f61":{"name":"plumier","version":"0.1.0-alpha.068e9f61","description":"Pleasant TypeScript Web Api Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","reflect-metadata":"^0.1.12","tslib":"^1.9.2"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","supertest":"^3.1.0"},"readme":"ERROR: No README data found!","_id":"plumier@0.1.0-alpha.068e9f61","_npmVersion":"5.2.0","_nodeVersion":"8.7.0","_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"dist":{"integrity":"sha512-t5rJPxhuBrDDZeonfwodvbhqTMlPak596+l7/X/2yeDGymO9NMPqyVGJuQe6H/EIM3TuP30G9JBkeNTuFcSX3g==","shasum":"febe3b9f3fe441694d3d10118df203145814773e","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.068e9f61.tgz","fileCount":13,"unpackedSize":65387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPzgXCRA9TVsSAnZWagAAEtEQAItTjfbvO1BrP60ppmGL\n/LKpnpX7wBntcZEk2nVy+JeoRDaq6ZAhpX29VOYti5NCaFpFV+KU81V8yy+T\nLPii19xFUcPOSm7ZjGcVv2F2wsvEnKk3QBlycOy4nLh69wAi3jZkP+MU9UMf\nrXGduEXYNcmBpdWzU6xkZO0hZsVYE+DrQQO3khs/GS4V/O7VDH5cJFQy1AW3\nr9gCeWcBxhNCJ8K1KFiwSFnk8rA+H/7byuE7ZkY4htEkiP3seu7mG29IS8tq\n27TJV+Ehi8H9YzJV18WFqyLUWIBMjuCdvH2LfnznFP8mlMQuRUoNWzL1NW8f\nrUwFrV50YWJ32y5yRGnw5AompRiS05I37B97DYHJ6K5Kec5HV08AzzeCamEZ\nI2K7ykS8BerNlkkYgGXZD+WZVG+a+hyPaaCdXCURuthGS3LrNSbmo0qmP4cd\ngkhcDko5w3AZ0gNhZTJfPx0YVl4FtiBvZDvOTd3l8ZlEnGtcET//EOucqTuO\ntjZYMbkNv0Wmrl9m8b80Oa6K9I7NaM+9Iv6/y8Z51cvsuxX9qn1q59JFheM9\nVUGO32tZVNIiNkodYP6BUWUmrANVaDVFOzVJ/mpsHLlFWRXmpm/0/pcvJSa2\n+5xw1A4DHPlq87xDO+kJfltATeHrvAqDz5bHpt32+Fo/RwwDk4FXJV9nMagq\n/zQx\r\n=vKB6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCO9K4lah/z4/yzG3/mQPBQK+wU4YvG6IJi6oTWNecW9QIgC0MCoAuZgVAsc9aH5LAwRQjU4RzAlwKNdNa5ThHDC7c="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.068e9f61_1530869783860_0.06833295164730235"},"_hasShrinkwrap":false},"0.1.0-alpha.b123904d":{"name":"plumier","version":"0.1.0-alpha.b123904d","description":"Pleasant TypeScript Web Api Framework","main":"lib/index.js","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","package-prod":"node ../../.script/modify-package.js production","package-dev":"node ../../.script/modify-package.js"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","@types/validator":"^9.4.1","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","tinspector":"1.4.0-alpha.b123904d","tslib":"^1.9.2","validator":"^10.4.0"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","edit-json-file":"^1.0.8","supertest":"^3.1.0"},"types":"lib/index.d.ts","_id":"plumier@0.1.0-alpha.b123904d","dist":{"shasum":"1a7ec08c0f9653ad87360f695986e70206f3154f","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.b123904d.tgz","fileCount":15,"unpackedSize":58594,"integrity":"sha512-h4cqtCF+3lLHAkmZviEoOdeGaUo/QTfnbXef+su5S6YrgIC9C05uBxcFOPhtcq2kBZ5PpZ+Xcn4SaRretb62fQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDI0AKFOQlC2DYLb7L9ZWssHxYifqdZ7tneCdeOGLq7ZAiAnBLBPQ0RZZu/uCE/S97CG1NmNdatnPOONC3xh5wr5Bg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.b123904d_1531019337035_0.45146769769868444"},"_hasShrinkwrap":false},"0.1.0-alpha.79af24d3":{"name":"plumier","version":"0.1.0-alpha.79af24d3","description":"Pleasant TypeScript Web Api Framework","main":"lib/index.js","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","package-prod":"node ../../.script/modify-package production","package-dev":"node ../../.script/modify-package"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","@types/validator":"^9.4.1","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","tinspector":"1.4.0-alpha.79af24d3","tslib":"^1.9.2","tsval":"0.1.0-alpha.79af24d3"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","supertest":"^3.1.0"},"types":"lib/index.d.ts","_id":"plumier@0.1.0-alpha.79af24d3","dist":{"shasum":"d297ed74fb10ad32d13f0e76ced4c966ac1dc2fb","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.79af24d3.tgz","fileCount":13,"unpackedSize":63907,"integrity":"sha512-rTXDIsy+2a1muW/PM770oKLOR74bMXlrbiDHtSMSZTrLTs+IEs1E0TKET/K8nTaxxg38LgXRE0wgVofPqCzS1w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFlDWV4/qwCePzoBEqBZEDAbCEHYDRCYOt3+UVmJGmrPAiEApdupZu30PNdStEJjN3fKBn5OtlXRMwHATXWiXdaNja8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.79af24d3_1531693594551_0.46790051511826447"},"_hasShrinkwrap":false},"0.1.0-alpha.caa0578b":{"name":"plumier","version":"0.1.0-alpha.caa0578b","description":"Pleasant TypeScript Web Api Framework","main":"lib/index.js","keywords":["MVC","Express","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","package-prod":"node ../../.script/modify-package production","package-dev":"node ../../.script/modify-package"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.1","@types/chalk":"^2.2.0","@types/debug":"^0.0.30","@types/koa":"^2.0.46","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.2","@types/validator":"^9.4.1","chalk":"^2.4.1","debug":"^3.1.0","koa":"^2.5.1","koa-bodyparser":"^4.2.1","path-to-regexp":"^2.2.1","tinspector":"1.4.0-alpha.caa0578b","tslib":"^1.9.2","validatorts":"^0.1.0"},"devDependencies":{"@types/supertest":"^2.0.4","benalu":"^2.0.0-beta-1","supertest":"^3.1.0"},"types":"lib/index.d.ts","_id":"plumier@0.1.0-alpha.caa0578b","dist":{"shasum":"8a741d62aab8e3e1fb147f1e1800bb0ccafac371","tarball":"https://registry.npmjs.org/plumier/-/plumier-0.1.0-alpha.caa0578b.tgz","fileCount":15,"unpackedSize":64327,"integrity":"sha512-RDAMKV8Cuxb2wBOxaScSI98wgcy5cmo0twpw/VBvqHXFUcwNd/OmjxPGt/dlt/wv0UKToc9HC9kdx11vs2rCBg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH+glN/0eFIdGxqeatPQRO62DrJ29GlDGxuvUMjG/TL5AiEArIeeCJDqLOEA48s1+1XXXuiRDGgNmoQsYq3+fg9e/WA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_0.1.0-alpha.caa0578b_1531696967550_0.9958079593147424"},"_hasShrinkwrap":false},"1.0.0-beta.0":{"name":"plumier","version":"1.0.0-beta.0","description":"Delightful NodeJS Web Api Framework powered by KoaJs and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.3","@plumier/core":"^1.0.0-beta.0","@plumier/jwt":"^1.0.0-beta.0","@plumier/validator":"^1.0.0-beta.0","@types/busboy":"^0.2.3","@types/glob":"^7.1.1","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa-send":"^4.1.1","@types/koa__cors":"^2.2.3","@types/validator":"^10.9.0","busboy":"^0.3.0","chalk":"^2.4.2","glob":"^7.1.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","koa-send":"^5.0.0","path-to-regexp":"^3.0.0","tinspector":"^2.1.2","tslib":"^1.9.3"},"devDependencies":{"@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.41","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^3.4.2","ts-node":"^8.0.2","typescript":"^3.3.3"},"bugs":{"url":"https://github.com/ktutnik/plumier/issues"},"homepage":"https://github.com/ktutnik/plumier","publishConfig":{"access":"public"},"gitHead":"4b6957aba9711118541af605434b005dc8f4c3a6","_id":"plumier@1.0.0-beta.0","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.10.7/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-1BeV/8xRPvI+dAquLVZLsU4z9v1IXwISLxG1h+ydXCLSLzgbTdlEEYaMJuiri2GRUp8Pugp9V+myxrjXsgMZKA==","shasum":"2f9bb1b86f6a064e8288618fc7b936319c77fe86","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.0.tgz","fileCount":15,"unpackedSize":45711,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcYjrnCRA9TVsSAnZWagAArWQP/3B4KFNXbI03soy80Kr+\nk2P0sHDlFaioX/NQt2GPfHJeowqXb3SAjM4vzAubhXHceoH8P2H6vTS0mr2v\nBUmy36eF9Lis0FA751QaR/bDKrRaY44BB7JDuY30OeKY9yVnxBq2qOOYwx8J\nUyIh9VMnisMsSBfnV7ReZrYKXO6X86JLlTHtA/nJwwANHboc4hOJ15kYzeJi\nwMMPk3AcJ9KNTG4EOdsS6OY10A0mtWLTbD0PHhUUyLds7swYVh9qngpE+lGL\n1dQLmEVV4ghbCwfmhuW2tWGd7CDm0EhbrQ7Ak2SobmAJLATjXKZo05K030BC\nhB+g+0Hw8yZ5r45rPVudTB5bHWRvAv/aoUInI/chk/+nnNkrTGHBU7HlF8Jw\nNTp3pDkbecyOn9vy79eLoYL5axOOfd2zOBeGJUlmpETGwcylhWTfbPWZZuOR\nEw4n8+ya9UrMcOGkPGN3aXsGVnQSdiQb1KQMk8Oi2W/HWLG1EtcgIt6lrqxL\nff+xiXsySO+EIUfAXhv2ID8oGO6N1w8BG82veiWxJ6H2wo00TZih4g9Lofl2\nEHNFdiZefEwf1sDLQUQjeSVJZcFmL59C24MpX9ZvceP4U3wGD3iiltHyTq6Q\n+hHK2KdSE1afd/VbeCJnIjOytytCgGkf/3DG/yfX9NomSJVRqj3dGTQpEOIr\nW/z9\r\n=XsIf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCgeNtVDitmvfgDVjCmnk5NdyW9CCyLmaxhS7yQEaCnKwIhAMfD+KYxyefJbHazWDykD/W5SdRQ78lPUc/umnEM55Ho"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.0_1549941479231_0.5394588097118844"},"_hasShrinkwrap":false},"1.0.0-beta.1":{"name":"plumier","version":"1.0.0-beta.1","description":"Delightful NodeJS Web Api Framework powered by KoaJs and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.3","@plumier/core":"^1.0.0-beta.1","@plumier/jwt":"^1.0.0-beta.1","@plumier/validator":"^1.0.0-beta.1","@types/busboy":"^0.2.3","@types/glob":"^7.1.1","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa-send":"^4.1.1","@types/koa__cors":"^2.2.3","@types/validator":"^10.9.0","busboy":"^0.3.0","chalk":"^2.4.2","glob":"^7.1.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","koa-send":"^5.0.0","path-to-regexp":"^3.0.0","tinspector":"^2.1.2","tslib":"^1.9.3"},"devDependencies":{"@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.41","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^3.4.2","ts-node":"^8.0.2","typescript":"^3.3.3"},"bugs":{"url":"https://github.com/ktutnik/plumier/issues"},"homepage":"https://github.com/ktutnik/plumier","publishConfig":{"access":"public"},"gitHead":"e717830e099e468c622b65557e308a5c591dc6a9","_id":"plumier@1.0.0-beta.1","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.10.7/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-4/LU636xvx18NIF5uir8yNx+LWQWe29eBPL/5LpTATEqIgEfCc9GNSXWwgT5MNnCYVLrhE0v/z+hn5d07L+ydA==","shasum":"27b5f9ffaaf04708c71174c236d31975611826df","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.1.tgz","fileCount":15,"unpackedSize":45711,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZhdbCRA9TVsSAnZWagAA1AMP/0DaPqq/fIV1301eqagv\no3wGKYYWLRN2ObIHi7dmYyrez/GxN83Om979PHsMHwT/6Gem4xMgaYiplKSw\n8poOI98oSoNrAn+m9HL01FjN9jKdbdEt8RaBRbJNSHM8PHpu8BORFm6Jp/nV\nTPjb6Zf9N7wmjs1mY89Tm6k2rxNV14GVqCr48nO8Jjj+1ufKuTkoi15gqUis\ncJI0jyDGDdwljDD5U7e52W8byqUeRZRAbV1P3tVwI4UQe4Zb6W6luZp0WN4L\nZc7rPG8eyXQOxcIdMEms+agYg8MXGzbkIY3daX6A3qSnAiKeG8ytVtPvij2m\n4/5M/2Ldy1ozUR3dAXy0OooBJdAJ2qhkgAGlINgQJStc+0AYy7EUJfQejsFa\nNEVpzsSgH4Rv4dnVF8wrtmZ0lSLyVJ04cTsow/zQNslH3aTHMVLeDipxIFLb\nXLVEI5ubszun+q7dBKbk4JGoKSosQ8AlWWXTtJ+9dvPqEm5cHPn8hY3bugsp\nnFQ+6FGxry+1ms8dhNRlAAt/L2TuaNFtEbqylxO1h4XRklLWc+gW86XYX9sS\nnL0bfdg6MVt2tOth+RncaCnvBPSVOwV8DBeEE0TIKVj8v0vnwnw0yCo+R/7h\nCsTdXbQUTPfozogY15KPAr/wm+jTch1aaqSpOMVR3FcMcMx4k2zeSjWc+hwc\nuXT+\r\n=TnkP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDhwhDdc044rNcsT9j9Ba7DaQRYHbYt1BXSKPktUCez9AiB+QGUO9US9bxscHvfoq9B5/vbXrBqH0cX+sRDte/LsXQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.1_1550194522445_0.12869500280646662"},"_hasShrinkwrap":false},"1.0.0-beta.2":{"name":"plumier","version":"1.0.0-beta.2","description":"Delightful NodeJS Web Api Framework powered by KoaJs and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^2.2.3","@plumier/core":"1.0.0-beta.2","@plumier/jwt":"1.0.0-beta.2","@plumier/validator":"1.0.0-beta.2","@types/busboy":"^0.2.3","@types/glob":"^7.1.1","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa-send":"^4.1.1","@types/koa__cors":"^2.2.3","@types/validator":"^10.9.0","busboy":"^0.3.0","chalk":"^2.4.2","glob":"^7.1.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","koa-send":"^5.0.0","path-to-regexp":"^3.0.0","tinspector":"^2.1.2","tslib":"^1.9.3"},"devDependencies":{"@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.41","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^3.4.2","ts-node":"^8.0.2","typescript":"^3.3.3"},"bugs":{"url":"https://github.com/ktutnik/plumier/issues"},"homepage":"https://github.com/ktutnik/plumier","publishConfig":{"access":"public"},"gitHead":"77652606df64045c73509de27c9d38e40b9a97ee","_id":"plumier@1.0.0-beta.2","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.10.7/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-Pkj69Io9KdIoK3KdLk8K7wZJnxSJGmM5gWHpFBGU2ccXXmTRgSJ7WPIibYdw22y22CGxvcCs732MbqT36gIqww==","shasum":"76b49ed1a2b7095ee969dc8da6676ad7e4957e22","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.2.tgz","fileCount":15,"unpackedSize":45847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZj3+CRA9TVsSAnZWagAAm+MQAJ0LU9h6Sz/8r6x5QkU/\naDwBSf7ZiSWEHtc5bwWiOUO+/eh5gt12JteOzgd85ZiM6uAxJHZz9RCj/vo5\ng+ScZJYs84y5D/IoXBG3zpAoiNtt6CELFC/92cqrzS/Cgng/LgVcXRKSLqr2\n6yVIx7YU+w30iR9SvQYTliUr4HceGvaGt+pfv5jWratg1Ez/lMmCtDKVAN4h\ne7IGuiep+uMKzeqlJREQp+Hw1QxsBARaxchm61k0boxc28D6XTaxc75G4IZb\nZbpE3Olhnz7f+YK5P1xlv71D0BbmsAtIprE+PWhjlIJnC7lmN2ZzeZDV6W6d\neYfcBKu1s7oVLVIdea62he1OchfaJCru89oGeumrAisk1k6XAYrY7IjTO1Za\nlOVbH4pe0GU9ooBCJTf/sBSPXNUGzCeVn2lppEs2AbUqU0edQoCtsj6E/ql2\ncxqMCyBMJyF11+j7lXN4NahM64R1as6JFR4DEBroI9u7xn5QdUnYNagBoX4F\nHvv7Rq8b1x9NN1L36jKq2Hkhrz39p4TDdtBIEIbTLCp31YGY6PZmU8w6MwRl\nJAQPUg8TLaMiFqAmQlXf3ivBZDRNI0bBK9kG3d1KZ1MwkX14udNk50kWTYw+\nb1Bihp1nmotS32bHRIgUge2vlKti3aZ8nd5oOYq0lSHQOtT8mZRAxYhfUXwc\nLMje\r\n=74B1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDmd8eJyf7O7juQNxL01dPtvS4TqAOj4Mc0+1wHAJ4zLwIgHA0XZrHwIsE3z1t3K/jiIZe6r6ZPHJZ6z6Y/4LnuHIA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.2_1550204414057_0.30275171519742927"},"_hasShrinkwrap":false},"1.0.0-beta.3":{"name":"plumier","version":"1.0.0-beta.3","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.3","@plumier/jwt":"1.0.0-beta.3","@plumier/validator":"1.0.0-beta.3","@types/busboy":"^0.2.3","@types/glob":"^7.1.1","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa-send":"^4.1.1","@types/koa__cors":"^2.2.3","@types/validator":"^10.9.0","busboy":"^0.3.0","chalk":"^2.4.2","glob":"^7.1.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","koa-send":"^5.0.0","path-to-regexp":"^3.0.0","tinspector":"^2.1.2","tslib":"^1.9.3"},"devDependencies":{"@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.41","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^3.4.2","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ce2b47b0e9ca50f330741125a90c54b55661c1ad","_id":"plumier@1.0.0-beta.3","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-5Ne6sgvEgi+Dr7GZkKIZCDUc7imJ/AcgkqBPdlC/DNzv1qSjatLIM9As1Bpo2j4JbA6nuH/zJ8B3ufNf+/4OTQ==","shasum":"8286b27391e70720356dc60b6c4fbce85ab18504","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.3.tgz","fileCount":15,"unpackedSize":54063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcisQXCRA9TVsSAnZWagAAY3YQAINE6PhntrU64Ojl4/DI\nAgSNBmWqmCZ8P9XxuPbHp9pDjjVilfX1On4DMM7+dhdtd47PBaDQpxU2dzWZ\nWrPp5DuPz4O+beZbOvyGbGDcotE2f7/Dl57BGGeLhBU/3U8HNKTb7n+N1cnu\nhdPXyzl7f/nCKNc5y4l5Q5H2N8YPVgM3W+/DqTqKJ2/b6MqeEbvbLexIGdOK\nBLUJ3D8Yh1d+M2Y/fyAZa+b3NoFO88omC58FV7KMoWKCBgl1HuoO0xZt3i1S\n5j7/Zu/gyKpRzrcW70R+PWZVdp4Zr++VxwfZYL7V37mbwIE5vuP0Mw1V9Kz5\nEplT1IOaggH8VrfV8xD6YOGU766AAcolGBSnkI1f6erP0R5fwMEazszj3FFJ\nZVZkX0UbbwR8x6AsV6urNah4hVNegw2sN46xLkAImxFQHYcjGrC2bktvNU1E\n4oEFOjWSvZopOr8oNIxiXpqKsPJEKkLzCL0X/ztg/uLel5d3HQ3DUvY38K41\nlC+6CXjM+byUCyAvsuy5YyaELxrsXplkWpETrS8oFc/uYY4kZJ+nxX1IJPYi\nnKS4J6jFbLMYBoikfCX/YeblPK6xZf1RF1Gw6iIMvcAoz/shIBiv/0tiEZtk\n9+gE3/sNSJB5WB7aInPb6BJAMxslS5XkVL88UoqDUnU3IY4+mKDjAnWVV39N\nva+Y\r\n=hgZV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD5BbW55Wm4DbTr4nI9Yba+rhcuPvSHtcfAH8noFfzZWwIgDyVgiJaa4/ewZCnDjp7VtBG5A3ZdkjeU6RNsVVASEVQ="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.3_1552598038983_0.2694233424289805"},"_hasShrinkwrap":false},"1.0.0-beta.4":{"name":"plumier","version":"1.0.0-beta.4","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.4","@plumier/jwt":"1.0.0-beta.4","@plumier/validator":"1.0.0-beta.4","@types/busboy":"^0.2.3","@types/glob":"^7.1.1","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa-send":"^4.1.1","@types/koa__cors":"^2.2.3","@types/validator":"^10.9.0","busboy":"^0.3.0","chalk":"^2.4.2","glob":"^7.1.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","koa-send":"^5.0.0","path-to-regexp":"^3.0.0","tinspector":"^2.2.0","tslib":"^1.9.3"},"devDependencies":{"@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"803ec103d9b72a40e53e806182228676a03bb0ce","_id":"plumier@1.0.0-beta.4","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-dOIn13hM44kkeH2BSMh+EM0gGmIBfa+izykTFhAQ2VUkw+oe/Ar1mjKzCQMxAfrGvZPLtVAcGvmPtNa+MECc9A==","shasum":"792883c554c2b91ba08d049d4757869bece2c319","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.4.tgz","fileCount":15,"unpackedSize":54063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJclKpaCRA9TVsSAnZWagAAs6EP/iKdRsem/SEKa3OFMR4Z\nS+nMeEmNCaOqdysdmbsMxKtd1WTP2ES3lil/nbTLywhxCNLukablzKXybgZg\nVfL0qN7F5g8EZzQRxz0ewaW0YYSt4zW93TLWp4oQ8fgHjkCn3bOReOOXHsoj\n6zMN/y1V46toPT+BnnwozKFdZmKz/5brvQQyMaxGpNrV4baYhQFnYFPHssOL\nyIwdRdHZhFW3nBqQAzHGRtN4BGEsEtufNIoqvEwWMPcQByKAUOYQciAXJZa8\n0uABgKPEBFUn4+y0Q8viFeOzQLeqMnmmUwb6iIBV7ZXk94ifqN0hcx4EbWo1\noPA/ueJAV58WGQ5Cs4I187nVNn7cfk7ZIZEHF3bv2/Bjzm2pQ42sKG+no/R9\n4Hi7Ir8Al2nHFutuncSAyA7fqapIxUOW2RcmG4iOEDd3iknPc5ly+RgNK8X7\n2y1l11R76DytwMGnjACsr+tiY9sbYpZr7bWerHsLplJHUQ+RjYmkPw0QYv/V\nlcasDjjyxZJ4odqiF55xV98AayR2JTtMNAwQ8oJ/RMlvhF7+cnBFdZaByYJb\n2t6DnlW6RXGUzsIAdBNPVbhZWED8BfTTTUO6KVQ1njJw5RI0zCdCAw9dJOLw\n67XjLzdPUagBeI/dHoKW/cJWLKdillfDXZNKNIA15eBRgWhHtYvDaVowldOw\nsbHp\r\n=R7rD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCNs7Xg+4nn02UoqPE+fQmkSZNyq6d4k8CRjl3CjTW+6wIgNExP+HqgZXDb7zvSVRG5PEuHHoPOiH1y2FjZ8/bv2BM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.4_1553246809683_0.3357461610137804"},"_hasShrinkwrap":false},"1.0.0-alpha.5":{"name":"plumier","version":"1.0.0-alpha.5","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-alpha.5+5f97250","@plumier/validator":"1.0.0-alpha.5+5f97250","@types/glob":"^7.1.1","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.3","@types/validator":"^10.9.0","chalk":"^2.4.2","glob":"^7.1.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","path-to-regexp":"^3.0.0","tinspector":"^2.2.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-alpha.5+5f97250","@plumier/multipart":"1.0.0-alpha.5+5f97250","@plumier/serve-static":"1.0.0-alpha.5+5f97250","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5f97250cf3b0e9378c65d0dcd14b1a7642402643","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-alpha.5","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-QEPOqInhe9WjIvv1kd9KP2OMcGcWaMaWi25s+4cHWmw5lLfzZganLugmTldnM0aNuNZp9zSPTzfKmoO5inF3mA==","shasum":"6d5572e54801a736c6b213ccc8045263497298f4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-alpha.5.tgz","fileCount":9,"unpackedSize":26810,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmRQjCRA9TVsSAnZWagAA07kQAIPbjBD3HTLThOkRO9Dk\nPJberazB/t991XtroCAi/WoPLMZKZ9D5tCdEQeEl1M4AK94/cYgEiIeRw7rn\ncgcpTrpZrvNnKGEmopzPUZHOh8X1aKJBgED6Bd2X+dE8Ldfe3oGz8bvoq4Fb\nXbbTWOGkSU4uZgxhvftqWX+0WDWpVUx+XVUHPSBKUQztOOiKfizovuKbj8v2\nc9BaTQ/QioFLTru4nTBdBLHTByfvMRBkeG4lgWwx8jFurXT0z1z9UQHpCGHF\nEp1CuJfHgZ4xneg6dhSUuovlgyZSfs1jepdMiT7DAa4Dr0qn/TkFw5Lco/D+\naJUo28SW/TEY361UFIP4dAFaK/+W9PmYjIa0dWASX71IfFNpjIvt4jTd9E/8\nza8C9iuQGmHkchL77mIAU0HJrflpTzTwgvNnmVJFV+4z5UTtW4gwLTZLms6S\nmlJRwmcpLK1TmrqXnlpAxHIR4sQzqfdHf9Z1kmNoHtxJNNMqev1fTHE+AKs4\nb4dYIdTgYTbX0VHMwP/BZcduSThwv0QkH3USEk/jL8fhgApAqspn43gvCLJw\n9o9OcfD52MbnIryEqJvkZ3mBfsEmvxILH2W0nBx81ck/SRisU1iXwrOmRtZm\nKilMkDzlWcnfE7h9bPtf2FxArAp46+NjndnqJQSFuZb3xyAzyYdLczHk3zPy\n7SJp\r\n=fH6a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFpXHftr4qfRw1JNKnT/0uzselNN2HT/xTZjJKzKQAKHAiEAhXR/iBJJlZDpfB5f+NxBYIcvJIi11EJk0kfJhy+qTbo="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-alpha.5_1553536035084_0.998644910440345"},"_hasShrinkwrap":false},"1.0.0-alpha.9":{"name":"plumier","version":"1.0.0-alpha.9","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-alpha.9+aa8e63b","@plumier/kernel":"1.0.0-alpha.9+aa8e63b","@plumier/validator":"1.0.0-alpha.9+aa8e63b","@types/glob":"^7.1.1","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.3","@types/validator":"^10.9.0","chalk":"^2.4.2","glob":"^7.1.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","path-to-regexp":"^3.0.0","tinspector":"^2.2.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-alpha.9+aa8e63b","@plumier/multipart":"1.0.0-alpha.9+aa8e63b","@plumier/serve-static":"1.0.0-alpha.9+aa8e63b","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"aa8e63b628454c06e6cc0da3c8ebfbfb7e39d20e","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-alpha.9","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-rATPBD5QvLkKgmCn9Mrs9Mjazuo3jleOfEQeqGOry8d7DgcgwVpXdQzig4mYN5GR4rwsbvlAVzixF2TPD2CUFQ==","shasum":"bb0538971e7935b324aa902d73e46ef2f3faecf1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-alpha.9.tgz","fileCount":9,"unpackedSize":27006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmhOqCRA9TVsSAnZWagAAn7UP/1m54Xu0KDlKn4N9LLFb\nh/2PiKM+NZrN+CPDgVlC3bImFQqnIOvsrf6m7q2OX+AoZ+dOes5iDYZc0umF\n2IvTAsQerLOntWCxHWnbZuZ461f8USW4mfOEp3WV6GTFDTn6PtwyD7XNZRca\ntmJ4pkfNLm/RecaCM0Ocm0yR0GksyWC/2BZi6K8Q1pW3IuObCcnBVNX7CiOK\nkipWXUl1kzk5Y+7VgWcWMIPp9WMw1+BgKdT/lfMWkvEtWdBIX2AY7NRQ7tHP\nLtqRjxyRA19u/EelDHh0sZM8dQuz8scyfmWtEp/K0nJpwMsjC2RqJiGAIfPb\nxjBgLcfwbFtSodBEZdW1AMD3ye3NnMLUBX/b3dcWIxkpKYKYetc9a2sxuSM4\n1VL0+B2vUxjXfxj3D5QW6pydaAWMHVwWDFs0XXlVzOzKG/PRKoqxeFt/tc1J\nZhdm0K9Qflb+HjMBrDPsfZQRXy1WDr/k2HdEphnlxWpueGdfk1foaO6DCAy3\nqlLkqR7rNOPhZlJUGVcexLjMrYj534hovbL4Te9Ym1YSZLm1tbLqE1j+XDMz\n2BjkoeApUNjV0Y6J+XKkgM8yHtpYkusu8crv2P7+3+R2jrOdporVleAcGWqU\n9KhUJrusCs+qFIajQIPmFZ407tQD8ROcRdDUFdGraWAA1Gp1xeDc3F4unuJw\nIYm5\r\n=Lf5Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCp+NPz9V2GiJFBqDpHeGr8iymG5gYocM0Z31aNMqjZAgIhAO6wnONUOQNxDUir+xhvUpSYEHS+ui4GrjpzhKA/3wTM"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-alpha.9_1553601449696_0.22384311666342982"},"_hasShrinkwrap":false},"1.0.0-beta.5":{"name":"plumier","version":"1.0.0-beta.5","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","IoC","Dependency Injection","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.5","@plumier/kernel":"1.0.0-beta.5","@plumier/validator":"1.0.0-beta.5","@types/glob":"^7.1.1","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.3","@types/validator":"^10.9.0","chalk":"^2.4.2","glob":"^7.1.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","path-to-regexp":"^3.0.0","tinspector":"^2.2.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-beta.5","@plumier/multipart":"1.0.0-beta.5","@plumier/serve-static":"1.0.0-beta.5","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9cafe1f9f7fad3ac68eabe06c76b8d3ba2ae3d4d","_id":"plumier@1.0.0-beta.5","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-vOQo668TvfliOgkbhZw44VrwVndjtkgwwD/nLhIS66LVRNPDTrCkPxpAeyW84lU9/Z4sGg7hYdkQ8hfDJVOCxQ==","shasum":"c3d5a1b8a4941faed87efcd846f766408e41e488","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.5.tgz","fileCount":9,"unpackedSize":26943,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmqJ2CRA9TVsSAnZWagAAbfUP/1vj+CtnbgBI1NrtjLRm\nNNh3/L+KmrSRMwZgdLCipc18HuTsEZT3FKLP+bMxe3q7cuBsbqD7CWV6MFbP\n4LW2A+3EPO2WcyZa8+TbL+jNvzlBSuWMGgumy4xqBn/4+xqUHTCKZyUQp6qU\nODrh6Y54W2xcX57mDclTiqFXrGCfnGOzz7F/forfiZW7dxhbZ375rY3PYZyT\nbfWJKY2BMZTZIHJ49RcaGtHzCQfPfJV5KaUhx3i0aPaAtgna1qeab5w8gOMU\nLsCxY87WCtJN4Jc4siX76QaD3/vb00PEhhe6MKAJqTNbzBGYibYEiYCve9zu\n4Ea98OKKbJZhkXssyQpO5TiB4tc7OJYJlqpPdP3OtBs9c14+KYMFiPWi53s3\nXDIj2xpUXt++z6VXikqZl9mKJN2LbNPJ7B6HertpMReXdvA0YdXAlwvaNoA+\n6PbuTOtzhSJZEYcnAtXd9kErt+bMugw+1JXJi7RmEZiQpKvurL488xaRi9Bm\n1aioojzjbTOD+JGUhvXjvl4XKIADRZ7hMSb1YRLTZOyp2uI5j9AFGFMjnPsj\nGAeJcWsDd7tSfgtx1HJll49VEc+Fziq3ObNlH8GTGz27tqG3A6uvmOqgSHrK\n78FAYbP4GmW5ksh03mhOJTvv2O3vPRDZjvESolsBFWg0VEErc8gyjXhG7XdI\n/DDw\r\n=XSsx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCfziVv+iB2h0w19O7T32rkg9IK2AUf3IM8A1EURfZWQIhAM6dS0aDDuMuEx+OOu7aGf1/wZUzL+ykFxgGl73kW/4z"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.5_1553638006413_0.23454058384343868"},"_hasShrinkwrap":false},"1.0.0-alpha.3":{"name":"plumier","version":"1.0.0-alpha.3","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-alpha.3+8b8e63a","@plumier/validator":"1.0.0-alpha.3+8b8e63a","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-alpha.3+8b8e63a","@plumier/multipart":"1.0.0-alpha.3+8b8e63a","@plumier/serve-static":"1.0.0-alpha.3+8b8e63a","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8b8e63a2369a79c072e1af78463c42804ca50b10","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-alpha.3","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-yOq/gATqD/xdd3ScXT3rTJdVWFEhra/MTlhkeP1CZINLoYe6+ce5XXh6uRgQEPLu5+1Uwzas3OGTMaa25nfYEQ==","shasum":"a639f450da59281286d464c38bc4cb402e8d6c84","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-alpha.3.tgz","fileCount":9,"unpackedSize":19535,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcn9kUCRA9TVsSAnZWagAAfCoP/15HBMGxxL+lPPkxqAeO\nDSlnReeTTq0L/8n9fWn6LP8LEyCabRmyD9x8DC8ZZ1MvuJc62fUo90mPEBcB\n0ntJoClu9gqjvVc0rF2zLJ8YwwdzdBOuK6QJqkyqxejtUkm19PKosTex/kBv\nA0gaeKp37WarStsbbhY5UrkZxZh7vE8jpVJ3hTMMr0hAwYjTnJhW4IemWp5I\n/aoFvJdVcdQLutxftpqc818FW/otS1p7OoOSp4ywUpPTN9yZNwwgXdN4GC6D\nQ/XIrb13fA58lrapQRfFAfAJFH21xU7AijC8h/bLKR100fy3bWN9j6CXkoPT\nMSIMHCaJNJ/KVQz6WY8+aKY/aIvKDk/vTZuc9/XdRWXbH4cUzArx8cphfq+I\n+P9YtlumVoiOYwt1cHAzG9R9jCcUPbrVZ/Ek5Tvnpn5Tr4P2gvq3N+kuI06J\noF6ff4rnyFxFu+ksBzNCbNiU1vSyMwJ/KzXqGHi6zZ97R4KcwsSc4dPMq0ob\n8kFyVgyU2Nv4jrGD8eyfDrEE33NUgy09L6UK4QMoaHM/MOZKb4Z3vF/mSJN3\nkFkRmpDnEt7L7ylh/LpkHoIMLsnoruoBOgWJBYPYym+5Dtay0/s/AwQtWoV+\nPhAqeyyRr2TMR2TpRRJTqHtYRQwPwalrX+Vh9SQkXppnhi2GclRJTS9PuAkY\nn4Bv\r\n=32Su\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHINeseYl/V+QyBRroAjuI45IhY8KZYqr7gShLsNh9hKAiB21shCNKOp8/1300qsdGejkx/yiNFlHnvgOjqhYFsOvw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-alpha.3_1553979668049_0.17069252239756172"},"_hasShrinkwrap":false},"1.0.0-beta.6":{"name":"plumier","version":"1.0.0-beta.6","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.6","@plumier/validator":"1.0.0-beta.6","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-beta.6","@plumier/multipart":"1.0.0-beta.6","@plumier/serve-static":"1.0.0-beta.6","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bd5081662ade3f49b4536f8bfc8d98eda941f5c9","_id":"plumier@1.0.0-beta.6","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-gvW0FxofWVQ8atphrSAoEJk3Fp4EfvCe965mL6X0sUraaSAz7J/otSY9Gchzo4lur8GwIOxMpD1TJPY30WLUog==","shasum":"c8c75ec40cc248d2e0e06b34713d1128c6eddab2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.6.tgz","fileCount":9,"unpackedSize":19481,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcn9q9CRA9TVsSAnZWagAAVkwP/0jaNkWQjcmkSWYBocp5\nAtctP+rtr+AvLt+hAq5Ve0IyTaJ5Na8Ebf1pEcubKHS+ou4F3dS8pMnmBFfY\nNyCRXevYns2dEKp8nBSboUUA3DRmbGdlmpzWDV5p+ZyYeFKoTVBw0/Ak6/Xy\n1Jdt7GKSc28SLi4r8qjI6gg5lIKD02ESdhEoEifIOPfpuQdB8pYFm7jNWB/Z\nxMXKgn2z5gNrgvTVmaa68Sphx1DLdP95yFH/DMDp3iWTXztRDsK02f49CPOJ\nikiZssvHns1kv7dXhf/p8hjopVxlxtHxg6CQi5qN7b4Ja2lvF4260lzT5D/l\nvWA7WTpb2/Kd/vpvHsbPJyBlS1F9lKiSdLGHu1Gda+UX/1NtY4gPypZUBVzm\nIe+WgtbR8KP3mj/OonI18Cfu1OAIhCLyr4xGbLyyt8USyPBdbVM6rtBKILAD\nFgOTByTP9IN7LjTJWygCo03rl3E8o8jtmKI13OiYRDCNsVpmxo8flc7VbsH7\nJVjzlRugpUYN6o3TVzSN+ksAdH/Qu1l2gTg/Hk09EQBP+t7YTQe6nVO+Xsxn\nmRBB2E4L5g03+1MZM5K7hEFL9wiBvZnIITFjfKR6BH9csa9vAQx/aqGHSxnL\n48etdVEx7mvDpGAtODMwwfEwLRwovshYrHlYigVyjHn2PvPOLq8d6zWAzzjC\nk5pa\r\n=6ysD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDGVsI7qOOGr8ELEU3kl7PIPfBIHB74F8rDrFbak94zZwIgcaqMk0NXB6Smrgf/myr7bQF/bbCHCyVvpFtrt4E6Mac="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.6_1553980092597_0.7626506023496418"},"_hasShrinkwrap":false},"1.0.0-alpha.13":{"name":"plumier","version":"1.0.0-alpha.13","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-alpha.13+9278020","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-alpha.13+9278020","@plumier/mongoose":"1.0.0-alpha.13+9278020","@plumier/multipart":"1.0.0-alpha.13+9278020","@plumier/serve-static":"1.0.0-alpha.13+9278020","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9278020291d4a96d9657d1b223d5654c8cbed0d0","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-alpha.13","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-zId7LjBIBKV3A5WGYN6NokfXQ0YvGusopQOqBtXDgQe/s2ol6qiWTv7ld77Sj22J4eZCnXtK6p89cKFb/iUS0A==","shasum":"384d35db6c3bcb64f33a9b51453d087b0a9a0db8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-alpha.13.tgz","fileCount":10,"unpackedSize":25219,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrpgTCRA9TVsSAnZWagAAnZwP+wSx58nKkw+CaeCzRLc+\nX+R7pauv10D+OtW+xhGJgb35M3wcznQ6apSCCm/iXaWk1WZUZ5/4Ov60LGf3\nZBF8HKd2teUklbxWk9AniUbLcEDRLMevmYhayf7ns0GbyFWo641LoFyjUnQj\ns82nIeHqa5zJnrA50dpCGfPhr25KUKnJu/qqJFp9EUurQeTvGJP3lXO8bLc7\nRHywQI/NQjAdxHVwl9hbn+pMXwh4YEAAjO1PUMDCQ9SOsJ7VvKoTOODJ0jB8\nleMPUWMpyNMTrvkFg52ewlZd/SmomKfRkVwVOSXRCDsBVTU+0chZVOmVm7Go\n4eRbsb3pN4LzMSS2b8Y8Omqa36MtUrGUejTKOqilr6sufIn2uxOa6jwJ6EP7\njDrzvNOHY/gddduqgM7qBHmsGxf7Dn4WPukYYisHHh4t388U0eDcZX+A2dea\nL8kyu3YvAAPQPzb/xyieVulIxC1MYrC95+xzPE7AoX50ZnYefP2AwTJq1e9w\nq3WpOCgrjCXSpXONq2zhTbUVBldUiOwtmKvvYSml+F81WL4uFY4GHIom8WUM\nbZWJtTo2EDX1MKq5d9Sl2ns8mB0ku7rqXDuSzywMjEUuIZrF/I3SoUiPXp4f\naDn6nbn304AiOELAL+5LRqkbAkuVRn2R5ZLZcFwKZN5WbEj/Hiq/GHBVyeJl\ngDG+\r\n=r8v9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAtVMLtAi9Pqtc9H8DAKpg5VyRjrs5MsmpeesJ1gXI6IAiEAnkWy/fAB0W9nM0AW/QCCqa4EGR25YPegtLgshY4If0s="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-alpha.13_1554946066327_0.4928494472786371"},"_hasShrinkwrap":false},"1.0.0-canary.14":{"name":"plumier","version":"1.0.0-canary.14","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.14+158957b","@types/koa":"^2.0.48","@types/koa-bodyparser":"^5.0.0","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-bodyparser":"^4.2.1","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.14+158957b","@plumier/mongoose":"1.0.0-canary.14+158957b","@plumier/multipart":"1.0.0-canary.14+158957b","@plumier/serve-static":"1.0.0-canary.14+158957b","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"158957b44b2ac6b7a88a666cefde89d7ccbae97e","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.14","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.1/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-zTStQlzaD8pB3kVvABaaK4QCwK/BzNRVOVkUnZxdu+6Wl6LBdgB1jfjpU4A5SedJs0BPoBj9TGaV9G/Tf5Xgyw==","shasum":"1b005bbf8f234ac039c6a7637c9415a0072a28d3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.14.tgz","fileCount":10,"unpackedSize":25225,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrqD0CRA9TVsSAnZWagAAZdUQAJde1Fk/fNwFMGA4BGJx\nGefZdERlkj1/VwIe1c0qJoXDl0OIlTxAAddmpoE+82r9Catw+zyt5VgTLS+B\nMTLAqCZzZswtKScEkVPGVeZnmfQy/c8U19T1PLHYqJ69YaWjviBQuSN+lo2u\nVVbLp6/AmVTi6YCoHl1dQvq7gNNfykxllhAPMb3YQo1qtIHNS0pJt1C0MkH6\n8rwy6SS9dRAjYamZDeDEy0IqxpmPw+B3QoScQ/1Qn8nqFzLa/vEnZg1zukdP\nvoxBXtffPhoeyCFd3THW1kzx+JAER68l6AuTE9/tlOeOntK3epL7IcXt44En\nKrc7zOrLBoIX1OsiKRU7CP5GYlZGufrzN7cXrhG3PzehF8r+0FHys7FI/dY/\nsuotZv0I3gcozbywn8NLX++9XovZduwhLHGVmvbLrEt3w6IWLrXy2rkcfhFy\n3mmv+9O5BNEyJLRoN1+JJslem8/cu2SK42fipS2kO0fL+IfDeLnwN2fPv7ug\nLRGn80834ZixkP3LMfg8T+XfZ7DuOKRfacLU4Ft6y5PkGByzSYhaKHEzsERH\nhrw/IXqYFrfSFFCT0mBFOebdOycl4O4snAVGgQit+KkEkdZomNwzi1h/VyvQ\nNCCqs9gqWPnC4RQJ3vd2otunI4Npr3I3A/XIz8gutcJ3gGB1ikNQuH77jjrI\nTL8f\r\n=SETy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDO/gyEOF9jcmVDqLM5MDnPSzRcY1Ma+opiFnF5iVUYgwIhAIVaRYZzg2R5Brk3PNDckIqfbPo/yANA6rrAvHcWvYiJ"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.14_1554948339439_0.3015111126982113"},"_hasShrinkwrap":false},"1.0.0-canary.8":{"name":"plumier","version":"1.0.0-canary.8","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.8+260b509","@types/koa":"^2.0.48","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.8+260b509","@plumier/mongoose":"1.0.0-canary.8+260b509","@plumier/multipart":"1.0.0-canary.8+260b509","@plumier/serve-static":"1.0.0-canary.8+260b509","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"260b5092fee325fbf46a43801d56b18c6ecba1f9","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.8","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.2/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-U0DB/oDClqjrqLYe+pSHqIzcVJSF9QRzKhNQcGG8QVCdVM76Dr9o3wtpdFxR75GA4P4VYc2hHCAYhALfloogBA==","shasum":"5199ecaa06974a1dd43f205163ec8999ae76790e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.8.tgz","fileCount":10,"unpackedSize":24866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsCO5CRA9TVsSAnZWagAAbxkP/32LmxTABpXLeqjlGKnh\nQ/TeqUbUWVl4NZhrMgcEjg2Yc6T3vKpzzxZ869ZpcMufBGP6R1Nv3fkcdDqT\nrZin2xsGSlmuw3byBgj8Xc/xqJRVFsKz/TX7Wvg60xt483cUt4nQhItJAcc2\nBScn2Qe/SlPaMzkFzB9ITYLX93tg3kECkVv+0nsz2GSsRMnnVuyQ76cc+SY6\n8a9rK4JIj3Ar/lzoL/8Sg+/n08hDbsrr62ebVq4ja6hOBbpMZ7aOs+nKz+2L\nbwFWl0WYncGy3ywpRgBrcq+5sTNd9r3s8ZKwTkQqBF8qxXL4Avfb/D+lDJfI\ngvjRJ5azDGgBetUVubUPPc+45DnMlvp3jHX+1RNk59cCmoZvlBeFOG+4V2Qp\n73xTQ9pCOzasPMAww0OL124YM+C6hN49/cdsVEfWVcB4PDIrlmSVeY8oBhz/\nifvDAcnPl05lM69wTNfi38XNkI5QS0GGvpnYWNcj5cwC0V6D45blykEsshV4\nlAHRljrha0i+PaV2ZwbEN7X9dQeQxjMumgOCReQ/Lw+NfugKq5McJLSD9Ri+\n+7J1XZfuk8t4uXoQsxRZh48vxz+Njk4rMyDZZMiomegz0Xi4Yh/IKjKjpAfE\nis4f3u0i3m82Sd1HSqfuRhoiAu21BNzMFkrDYryp6aZGdcsksDrNQQ4fG3oP\nVUC4\r\n=dePw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEsmZKM8SOsi99peIGVateEgO/h3kRXrXbiPE1fi5ghwAiEApxM03o8jWEJg2mJMbPyZyCWze7X+NttO1c046EgFMgY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.8_1555047352561_0.581261387826586"},"_hasShrinkwrap":false},"1.0.0-canary.9":{"name":"plumier","version":"1.0.0-canary.9","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.9+6d3058f","@types/koa":"^2.0.48","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.9+6d3058f","@plumier/mongoose":"1.0.0-canary.9+6d3058f","@plumier/multipart":"1.0.0-canary.9+6d3058f","@plumier/serve-static":"1.0.0-canary.9+6d3058f","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6d3058f8e4d6b94137fa9c3b169122c0bbde25a7","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.9","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.2/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-846emBhQO1A5pWhppdY4hfs2a850jjtt8YDjrglHvV4DDfk/HXKpv/LUI5Zt43jp65plOnPgPTL0SOLszs58lA==","shasum":"ff412925667cb617dd7e2f266824960b690c9b5a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.9.tgz","fileCount":10,"unpackedSize":24866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsFQKCRA9TVsSAnZWagAAw0wP/19NHhAAw3IgJvfL52XY\n7u0Vk5I0H+h6zDyki67XgkoYc7xmJCuCdr8axjTjyVHv7KObSjS72bgeWuac\nGei40ruRCpD2Q0x3byLAd+7EH4opJfDGoX3MdTNDsldBWWDUBI0hSJeeebml\nmbdoaXwRoLjYTxov4Ac88YduKWgsJT1XpyW2+BHYUE/OyRSlzVUKzqEH8h8P\nhShPaaxgnRr/JXP1tDFdQENiSojL31KxN0GNzHmDFa29zcx30WZb/7FjC4bm\nnjDZF3ZTCyGw/cayiBr1N33PfcNKNJ4fVa3Zth41hw7Kq/J7qXnV4dxPleTl\n3Zfsjp8Jr+BftXVkLCtcb2EimTAtz+FF7wCOxCGGiaNjKj/VzaZ5fFVuxBhn\nYM4Lb5YaMR9L6uzgZgkYDwuT4Tk7oNT0nwUawZzp1cM+m8S61g2FG4L5+B1T\niMBf63EDYnfXVzaiEF9+EuLADCn5YhMVHujDMbTWxPqLHPRbL+ypsAlHGmxO\nAkJHfCADzJ3ksLuiWHtfa70ayHj6+Q6QUrvkb0+1gIjRZsd/VNvFYTH0dP15\n6+qN4e+8KX235wr0XZiGNBTU3zuVOvXVxPuBDH87O7t8NvYnoVNJKA4dQWMw\nOFFrZbXlm7E5NMzNPf8a6DdHniIs8vJhbJPt6q9LoPcKsVMjMr3zEvbtK5lG\nX1TP\r\n=mAjl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDoLF6tS+6ZMv82mglUEKlPAA2WQJVT8l9dOTw2sUWeTAiEAl8ZKBb1cy+xVbST6EKmwmsrEBieaDv/qXUBDIhZmMKI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.9_1555059722249_0.32728376763973865"},"_hasShrinkwrap":false},"1.0.0-beta.7":{"name":"plumier","version":"1.0.0-beta.7","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.7","@types/koa":"^2.0.48","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-beta.7","@plumier/mongoose":"1.0.0-beta.7","@plumier/multipart":"1.0.0-beta.7","@plumier/serve-static":"1.0.0-beta.7","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f031424abc34091567b13d7ebf614805f773ab18","_id":"plumier@1.0.0-beta.7","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.2/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-6sCfgwxody9Os8Kvx0Ab2oQ/SCgND8KXFeylrQtYHq5Md43AkQP5eQN7lAJu35BiiTUh7vzVas3wuPUD3DtxWQ==","shasum":"65fdc1b541f9e48c8d8941757b8790985adfdc8d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.7.tgz","fileCount":10,"unpackedSize":24806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsGPbCRA9TVsSAnZWagAA5R8QAJLsgfgGJ0cJ8oB2wS7r\nQoOFpg7950Up7rZ+AHhNSbHNsDjGu7lXdUWJbfVLtRwg0HynCxfK81+XBES3\nToh18EetBMd4COLpNv2jGPuAkrk16FRr06YA1KZDo7NkkyKBAZWy12a4UGnb\nymUGQdul58cK4gGbu2ntDzQmI/zprh5MDkakaJKl6n75oH7Uts1pHA223gDd\n3Qq33qsffxSIl3SI3Bdmh1NhohGrM6q37fKYq/XvzMFQ5POWvucQM+fMAqSI\nquKwpz932oc4lnZ+Q/MY+R689bD2c/K9v83ulXXzlNegQPwtgbkeaTo1QsBs\nDJqgwblsTDNgCN4Xh1Vbc4/u91ATpTYaOQZO8XVPPSTjOLvD3udAkKHcI/8b\nZN4DC7dIV58QZfrZviLc0yhuRbOcGlJGWkngcRJ3uaK/9x5YdpXT6WbbVcrv\n1QQvcO1M2E+IC0WFYJ26xBAGQdxUhCDIgLmc8Si6T5SMUUJ1vuA+1cvoj70l\nw9Ka8/w+HPiwlIWQHJe8HPESu4up1xKH/QSJjma2XyuCOkvKtzMbtUMjwvqp\nPsX0YiIgqMkzsDeoieMnWqVEG2WU8xBijmHn/BjvztJ7MYg8K0/BAnaAC/N6\nNCDnXarsZB8+2aJii56bPlE570KPhm8fsuqw79/DP/pf/qeWyfdbc+f1Suyk\nYC2M\r\n=luTr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICTbQklHTxHVd9atPepa8WgZBH06pe3P6WeZasEMs3SnAiBr8mEfhSMrtFHveSozkykOA6lWUsO56y0wJgAbgJ4lnw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.7_1555063768325_0.3886101602913843"},"_hasShrinkwrap":false},"1.0.0-canary.0":{"name":"plumier","version":"1.0.0-canary.0","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.0+7c82523","@types/koa":"^2.0.48","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.0+7c82523","@plumier/mongoose":"1.0.0-canary.0+7c82523","@plumier/multipart":"1.0.0-canary.0+7c82523","@plumier/serve-static":"1.0.0-canary.0+7c82523","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7c82523128405e9cf113f83523711c4b715b9583","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.0","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.2/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-BghjzCblh3diR0xCMGO6bMHM4QWqSfGQNj4CrXLyPaNZWZeju4DKHqzaS5CLaMLeApvvG/pMBnCRCkr6XRZY0g==","shasum":"779257f001cbbe55c8c07de9ebec8421e0012474","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.0.tgz","fileCount":10,"unpackedSize":24866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcs576CRA9TVsSAnZWagAAsH0QAIDHA1y6OHMS1Nx+v3Q1\nyAjMWNSW0FEUyoZzu4+RSCDvlVEIKhcN1gIEcJhLk2m+XYbkYjDwhcqyOcir\nWUvhWs7vOlBAVxylNqlFZ4L/CXaTNc/XAHMxf2KL0VxNm9zcUf+r8/h/Ko+d\nAl9kqxGr/KL97g53gLtTmoED0i45bRvr8yKsUtquGFbTdkecnPg9pH/ofIa9\nl7qbThTLJe32hi3MbRqFY1dNExiKUHJAOQM4DxNirqJ99vvG/xq1wbJsSmIL\nz38GGRgQzLBZueVUzvfjEIKnyQ8fJFHDqWhiQVffkMcIQd9HkKaQqRrqfLv3\nZK+3tlj6zLGRajNl0fhINZ5Ughh843RNWOe2qQ/yG7+L8C1tZsZ0f5Er1esZ\nAEN7zYsXT4sU3h+UIfnqL0h+xQBZo7ZEfZEo6Q+v1G1tHvrTK9ix+PHR24AL\niONrPcZRBY2Iq81As5WN7Ot31lYOPAP3F/Jb16Mpd6sBBrB4w320yxe7cYXF\n941VcxvEy/VpwGcfuZe5K+CGXWJ3lBiy3ly5K3jN12KMgljDoZ6FVKE8pYEc\n968fel+sWGVwH+N71CnFbuIRRAI3lDVu4SbA5wgJcxSIB6skwc3RWiwayO1w\nwfn8pPGJODapI/DJM3WGC47OKs86TbSlWExQIhzSTqUmoI3VfDYT1jSOLNBL\nFu+l\r\n=/cLi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAKeiMQvgtYsrXTDCPcMxzRi2Toz04O82VTLVXSzBDEZAiAp65NDL8ZYQl1G/Y1q4f0BrWMqjOaDhMISoEuselA4kA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.0_1555275513258_0.6380513290423901"},"_hasShrinkwrap":false},"1.0.0-canary.4":{"name":"plumier","version":"1.0.0-canary.4","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.4+953ec9f","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.4+953ec9f","@plumier/mongoose":"1.0.0-canary.4+953ec9f","@plumier/multipart":"1.0.0-canary.4+953ec9f","@plumier/serve-static":"1.0.0-canary.4+953ec9f","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"953ec9f6fbac4d0d40c8bb6e2dd0027e033a1ed8","readme":"# Plumier\nDelightful Node.js Rest API Framework powered by Koa and TypeScript\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.4","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.2/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-eEyDx6iE8QAumh6WOliuBDx3E2SkajwK0Hux1fFZkr1iBpWTmfsLFwlz9YHDsW02BHq9zPTzH9TXgNItxgJ5qQ==","shasum":"4ec7e7e2377efc525cb45578e2ebf720f0b1ebf4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.4.tgz","fileCount":10,"unpackedSize":25075,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcvGtYCRA9TVsSAnZWagAAkLYP/AoE1JutULEabp6ySZSF\n20alBslhQKtuc6RSnEmdv2TO+/zM/An1EQAV6J75vIi2hFd8+Q4K2ASAelkE\nWPAgNqzy/Uzy6Jn6zte2PaTqY2DKcirfrQdqbe+ZUUtexevweM8rkl2f86/V\nD4C9WWzhba5M+lYd12qe6afkU/EWxjmaqRARJjfRYke986MDp4ghFXqWydyP\n4jDQScW69jRrhtBgwac3LWMXlL1mIwxbP5iJCtg3tzggoxOHVoTTjygE4uvG\n+eCZZoqS35XFZS78ZClwbW8nw67gRPPT9BFb9bmIvd45ugTNEYTVY1BOKOIh\nO3n4zHHqJVM0FqvDWD785R5+nbGZjcwb4MirdawlIWkvaJvD6PMqfMjeBkwz\njNNtzuS1rAa0f8erDqd3SnY6hFLXqNFy8ckiMlcStDtd1iVV8b8fWmjyTNu9\n0N5A6JWscLrltPMXn8rw1WaPPYGScrRNDiNSEQVdb38UwO4S/KrQkPkI69nb\nfp5Y1PYRftmx/gYVR4J90Vxhrlq2RERexOAf2OB4NpvfWkel6Kq6Aq1sozuV\nX9AbEj9gpJsXT5sSW+h65ZududeOz8HpBVR/j4dF0QGS9scO7rhZjqJo6KW0\nEF6XFWVbBpuwTXq7ZFjGsPnCwDSCk+cxV4L4CBVwtuqvwIGFcT3DthNJPYqJ\nhMfz\r\n=H3Fm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICop3snix3gL6t8w9xyY+QitDlI2qain/KS1HK805NQIAiA43+1QK43BoBsqhbUaVDdsSER6tYPIh7MMdDxmb3veDA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.4_1555852119685_0.5449248761634653"},"_hasShrinkwrap":false},"1.0.0-beta.8":{"name":"plumier","version":"1.0.0-beta.8","description":"Delightful Node.js Rest API Framework powered by Koa and TypeScript","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.8","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-beta.8","@plumier/mongoose":"1.0.0-beta.8","@plumier/multipart":"1.0.0-beta.8","@plumier/serve-static":"1.0.0-beta.8","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^5.0.5","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^3.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.16.4","express-jwt":"^5.3.1","fs-extra":"^7.0.1","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.0.2","typescript":"^3.3.3333"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a5c4a235f06a567eb3eb8976f99a8bc0b3fb9d21","_id":"plumier@1.0.0-beta.8","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.13.2/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-PLN2mLpOq/DtdbWTLEP9dH04qpd8t2m6I8T3a+DQd0gJgmV1Ec0pBhw39OlbTtNLBYbjSIcThvjL4QyHIwPWVQ==","shasum":"1c7290d09d5400a9b9cf3ca9449084e0f5842b36","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.8.tgz","fileCount":10,"unpackedSize":25015,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcwO8TCRA9TVsSAnZWagAAIYQP/05O/hsJCQ+t9BUGTXIq\nWoOD5roJRiq0laznoD6IisJ7OoHltbbkLxaAgxJ/AinIpONPLZpBlpCj6Lt6\n5MWTila+pJrYMDmZPOSEtmz/R6ryWoDBEqNY4uPthdPDZg4fN9YpCQn4Wxqq\nIZjNDJD27QGfZf5p5dB4ayGtSQK0+18MRkDcM000ODbLO8ZADbTrLYM42+3b\ngsNe9QnyBTJlvPZWyKhXTMoGGF8Z8o7oZoP3pM5fNlDkA42s+k0VWqVgcX7j\n6YsSOjNgXgDvLQaK3OR5CutKH6k6QNDiXYCQtfq4IYVNqNKLbh4c44e+hSOg\nq5uMCTyQtBNXKukV8EKANTXPpEAGhjijn0uyt98hd9Kgdpm3DWxi8P2cJhrT\n6OCfl63QZ1V2xkGbjyAIJRxuf/Jw87Sb22qvOWA5FXu5ULW7pCY/D2xwcksK\nEXeNBXf5EiGWwTR69qpw87bMvqFunoEId2FCUib0VIeHCJR61lyTkds1hllE\nAdwaIPbtvgp2/wpOv5Icf3q+5fNgHqFF6QGnzOOwWak+6fg3Kx3I2KT/mS8L\nTpMjkwflQAPOZ5/U+Z8b5RbwaMsGGCnveFIAtx9C2MqaqACTxbEUgrfLDnvg\nAIeUz+2LBByGJpfjgDPkzIgzqWHdM7/nyTcXzejpod4QJUidu6z0RK4/53Ee\n9CHe\r\n=L6sF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHziH4JA4vh78kIOI8UJ7VjJ1J2aHWdWlDWXWYtoSwtQAiBgO5xucisPRKkVZ1qVOKxGep9SBVFCJRO0Hg9nIHJMbA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.8_1556147987108_0.4881024018079758"},"_hasShrinkwrap":false},"1.0.0-canary.15":{"name":"plumier","version":"1.0.0-canary.15","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.15+d64f567","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.15+d64f567","@plumier/mongoose":"1.0.0-canary.15+d64f567","@plumier/multipart":"1.0.0-canary.15+d64f567","@plumier/serve-static":"1.0.0-canary.15+d64f567","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^7.0.0","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^4.0.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.0.0","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.3.0","typescript":"^3.5.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d64f5673d593b07f710ab5e93ebc5aba3985726a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.15","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.14.0/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-IQO8G1YsrK4xlwZfCvdWRJfant8TrrT/JKcVoMahjkjBedyf9NR0Mp6ORZdfvvuuyBlzVYGDw2fTRqF4NGAoXw==","shasum":"6a9e7209edccff24c3bcb749730043e2eead5bb7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.15.tgz","fileCount":9,"unpackedSize":18586,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdHZv/CRA9TVsSAnZWagAAzrAP/1S49pP3bd8vGtRtZE6D\nM1J0zcXqP0qBOQbQHlClNNm4kZtvAdSCe4RMM/5fcS57vsluwA/R8F8Gex6l\n34kDJJOQZ3A0V9xzv1DTgOBZPWXAwjX3TxmF+EZDvJzB9cHS88RKd+4IXOJZ\nYSo5m5dhjpAe1JtjLFfqj242ZhQdJAQ1wIbUxnnt3MMVBns8hLdJw8KGL9zF\nJKqg/iNXswfs7D0xHyu+I4VV6QmBxHdFHoxjTrSO+cQYYuI4ENCxGFKIV7Wr\nidHSgp1RwQpVmA/J/xhx+pI15fm2n9kF9K0FhqUReYZaJU6nsnRnVyB7Bkbg\nA5kKaCX2aS55+L4XoWIQ6Un92ihkVIf/QzZolPVbdqTwxG0SkcKx41pf2oC9\nLCGP8grKvW1ouLBQJP7QGdWBEqEexE7qxXAly6n2wrZ3HzEEwRD1EESViKfF\nqRMkGN/kaOjbAnHQEnTXto3hZs7OeFaDnNYJcG/fvhWmXZKKCln0E/xkLzBm\nixYKLH+TjSZjwk6KHBQJFyNlZzf9RfSMXNAQ/BBjmlHLf8kolxPIDQjR/49v\n8S3KTsuqKa59CyC5gzvH2BxTC6smFqZ1ZzLLKyVglElmIOoqDw/EzRSfIhWE\n7d8Xd8qsk560Mx/gxqzaAEeGNydJLIH1JRW2hrWHVN7BdS7Z05Ohc7PScR6x\nofMX\r\n=NXGM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDrQi7wT8UEME3bookTwa5DrkTExwuw49c8E6kEmCux0QIhAOfK+EU+BB51GvSjt6EH7J1qKAdmPAF16t+20jarVWEJ"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.15_1562221566416_0.5203195783518832"},"_hasShrinkwrap":false},"1.0.0-beta.9":{"name":"plumier","version":"1.0.0-beta.9","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.9","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.9.3"},"devDependencies":{"@plumier/jwt":"1.0.0-beta.9","@plumier/mongoose":"1.0.0-beta.9","@plumier/multipart":"1.0.0-beta.9","@plumier/serve-static":"1.0.0-beta.9","@types/body-parser":"^1.17.0","@types/cors":"^2.8.4","@types/express":"^4.16.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^7.0.0","@types/joi":"^14.3.1","@types/jsonwebtoken":"^8.3.0","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.39","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.7","autocannon":"^4.0.0","benalu":"^2.0.0-beta-1","body-parser":"^1.18.3","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.0.0","joi":"^14.3.1","jsonwebtoken":"^8.4.0","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.0","ts-node":"^8.3.0","typescript":"^3.5.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f5ac7822228bbe5ef1c3e4e825c03caae1e76033","_id":"plumier@1.0.0-beta.9","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.14.0/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-ncf/yPa80AJMZzDrmPt+cLi8tsSq38egF9LB69OoC7nxKfFVJPaO73bAI+6whBXy3lTMmvGfhpbZlwKjM9aomQ==","shasum":"7873a7db564ca85e2896dca1baa368198ba30b15","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.9.tgz","fileCount":9,"unpackedSize":18520,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdHat3CRA9TVsSAnZWagAAjIUP/0GyVMH9eIorq4JEU5ej\nB5SOVKDojBvkzS7/SLa7qW2Rceuf307WKedZoS/lLxhaLL1KlLaOD+Qgdtsv\npMGewXiEA6RRjHhg9qHqape6T1c8rpeTeyXoqc8AQ6An4XIFlUjrrj7a/jOr\nmW+oJQquuB1gcMtOvvaZae6JaAxhoLrY4LjGpS0/Hy0OnyulIglKKfTq1FGZ\nQ8meoD0TVTBZPc6r0RgwDkfISDUwrehT+if82IBoIO4bwMN2dLhQ+rGkWFug\nB0R1CFciqYsFHbPEmBSi87mvWfdZch4Ek0XuP3bZ/zb5Ca7t+NZvcgDsd1hc\nTFcQOqYFnB8j/tzlO+ZQ0Sl0zoOznHQcAXbP0RgCf0we2pPSKEfDbWgEkGru\nHcMa5mncsklAe6Rnc9pH038ksoWKyW1QHrHB3OEGF3cmU+izk3n1CZyiDeem\ndppd/O14lTnP/E0mog1MC1XuPLUFXJRWs8ZDTYxflLjfo3BIPbW7+D7u6KpJ\ngZeKbA0W5+AV2FnMEdsMqcy9pCbAW7gJD3iPD2eF2HuforBQ8e+yIIzL2tH2\nCaOyPIbZRA5kguNKJyt76yIBFJxBmOcfjOjX1/e0vAVIIOWpenROcDJqV5c+\nmDYYqIbOBQJ2Ym3eysjVIn4Bi1jBzsMiIkC0lQrtKxBCGPMBVkaMEqok1oXk\n6O/a\r\n=DCTi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHqP8ljHPU1F67PKokNi+DdQusrFfqiLFXtICFuegZwtAiEApU2ILXNeZ1nGOTjhjzTZppwV5/2bzZWqzlZ1VG6K8C4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.9_1562225526702_0.8802431940001791"},"_hasShrinkwrap":false},"1.0.0-canary.21":{"name":"plumier","version":"1.0.0-canary.21","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.21+9ac3136","@types/koa__cors":"^2.2.3","koa":"^2.7.0","koa-body":"^4.1.0","tslib":"^1.10.0"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.21+9ac3136","@plumier/mongoose":"1.0.0-canary.21+9ac3136","@plumier/multipart":"1.0.0-canary.21+9ac3136","@plumier/serve-static":"1.0.0-canary.21+9ac3136","@types/body-parser":"^1.17.0","@types/cors":"^2.8.5","@types/express":"^4.17.0","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.2","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.0.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^2.6.3","supertest":"^4.0.2","ts-node":"^8.3.0","typescript":"^3.5.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9ac313602b87192d847d455e121a5ab2ddb212ee","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.21","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.15.0/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-6hSD/IXzAesBziHHsUa/TNvyc2IHrR8divKhkucZZmwclkmfrB3Sar+/GRHNnIHDWtFeL+9ujw3t2EOvyhPE8w==","shasum":"77228d53487f6d245bf0a5973edf39f05d24f81b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.21.tgz","fileCount":9,"unpackedSize":18271,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdLBFPCRA9TVsSAnZWagAAOaIP+gOo6zRbBrGn8Ggo/NCx\nhJJiiLoAUuZG6PMB9rDYZ3+77+SBnqyiSZg3jTgpDlgg73jOMMJcfpkF3ktl\n/4sWw7zSTNMCdQwIgtmm10tmmWs7mrMvDtvrvzcOMu8IhP+WIK0DsiHd8LYE\nML+6Zrz/iizlw87bDr/wxjRx8nAPuHOU21mXLcfIg3eA+OswBCpOUAZWawoo\n1CywdorHQvkwXHAMpODCN/S6jZ+MIGZ3+BFw8SorZL8vPf+7BIzbfMDOvH5J\nCuuTwGPaS63J9Fg1xKO9CylrT7LEzAsFdJlelj5AGQHI6mAcvepzn89ovyL3\nhrNuAVSp7AHaWENJDAb4QO9fYYIaNGQcdaTqcayuKA5iykfky+O9AOGzSTGY\nrFM9VfnxZCT6nWkCX5/58fB3TgI6BI8lWWuezSORgn4WLXl5sXvsQS3CiXRL\nj2/UQRf9xDWKcZEY6YYBPV2t/c5mhcaSDYftsRqr2qSXhGNN5cGFjoo1xlXj\niW3bjfOO5mYpJbDVsMQorf+33cZ9Q0m0cQLSPEwlMpA9D206QFH4v//IKSie\nDw/9FIgnOHaEksiVYLqjYxzPTfiBCdF/2SMKfgYWbqLqZWTLagJ8TrjnQaa8\nChZ8mjAvmx/IDHdGLF7tZHhUWwtwPOlKk/hoY8Hn32LYmf/Zwitk81qhZFWV\ndAPC\r\n=2D9/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQ7xL8ErCay3wTN6V0aypAQ0wMV95FhaUJZP7YeVJb4wIhAOOnC63zXzVJvIBeUlUJz9yGA0I013MbElinB4LY/YVr"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.21_1563169102867_0.007665485222198676"},"_hasShrinkwrap":false},"1.0.0-canary.26":{"name":"plumier","version":"1.0.0-canary.26","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.26+44b9a2e","@types/koa__cors":"^2.2.3","koa":"^2.8.1","koa-body":"^4.1.1","tslib":"^1.10.0"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.26+44b9a2e","@plumier/mongoose":"1.0.0-canary.26+44b9a2e","@plumier/multipart":"1.0.0-canary.26+44b9a2e","@plumier/serve-static":"1.0.0-canary.26+44b9a2e","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.3","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.0.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.0.4","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.3.0","typescript":"^3.5.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"44b9a2e802d61161f03e29145bca163dccfb1bb0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.26","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.16.4/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-KRrBo5nJ8nkTnegjpNXL30L+f3mb94JesL7UIfQzrGV39et3rUaLUU9ACJ3Hc6sw0QwKToB6JHEEEdBiIgDcxw==","shasum":"450b051c4fba0fbcb1d6edc3ce6a40c0221f9afd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.26.tgz","fileCount":9,"unpackedSize":18271,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdf/CvCRA9TVsSAnZWagAAyroP/0M0jkwya8VcNjDIkJ8d\nIV1aN4gBnXCVXq+Hhp3jXin/nob0mqC73VhnNUzuVf/trp7wbrwfiV+KlDMm\nIeqiATWU00IcnZMsZe4qurR/sqkMZcfz+0A/pVaHKOFld2GeIGjH5WAcOxOj\nDKp0OHk5ah4IUtsQBkDAC3Qsw9FBN65HmPA53/bej1uZhf/Kzr70rssnsgQH\nrek7SRvx4M9lOrbOUK0z2zY+XaoMc4zsXl8w5W1F0glfSNU1cxmqUaoq3R14\nRdxt6TvoJVEEaa4t8GaMqd1qY/BSNSZTsGM5HWewJzb219HQbmbs/m05tv+s\n2ruh0mL0w0zq7gwfQrILA+b5XNb157WBZsAUfkvKlSOg7AVEJA010GyIjbb7\nVAO9sOOzuDBULjZsxPLpb7mSSFbR/jhYU94pid7612d8eku8KADCthSJ3Yf7\n3GjBAKQa7t/sUGpYW3iMM5tnG5ZX1+TnzfOJvxaluHGFWMhN2Er5BXqt0kGY\nAGkzDobhFY1sISeVMcOB875X5BL460lbqKNuoBes3hzLHfoLzTdL3ix/yRtA\nhHu3P6VNMhwyy4l9jPVybNtlEzNChHkCRd1aeS2pilUR+aam09sZmD1Vs91t\nC8LEg8iv+iH/excty88EcRfK7RCH/Am0NrPicvb+vuFsDh68CwB97RqvFq7o\nE3tF\r\n=N4F8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICg/XajOL1kBxPaXp6l1On4jxqw52O1Z77fr+wZXR4WAAiEAvYcsIocyBJYrGoTjKDgifG9oCBnM9efGKWGeUrQrEyY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.26_1568665775372_0.04796759433941067"},"_hasShrinkwrap":false},"1.0.0-canary.31":{"name":"plumier","version":"1.0.0-canary.31","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.31+59f5bf5","@types/faker":"^4.1.5","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.1","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.31+59f5bf5","@plumier/mongoose":"1.0.0-canary.31+59f5bf5","@plumier/multipart":"1.0.0-canary.31+59f5bf5","@plumier/serve-static":"1.0.0-canary.31+59f5bf5","@plumier/social-login":"1.0.0-canary.31+59f5bf5","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.0.5","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"59f5bf558c5d9996a06c5fc9f59a75b0c64142cb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.31","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.16.4/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-Ns2ZD9ussBjaYDzL8jU9hvFgixGRy+OPlCEQXeFJAjXjI2m/b+p0dqS0gyP7mb1Qqn5be3GFxycQx1xhcQuxYw==","shasum":"7afef203db345ff581cfa2bd8db0a2e697fd605a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.31.tgz","fileCount":9,"unpackedSize":18445,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdjRPzCRA9TVsSAnZWagAAhWIP/1o6+DTDAIT7sIUy2TI8\nXCaT+60NMlAXurCQIHglGiBk6elYft60hmbwfISMWGkgXlS3dKgN8rcabyCd\nmp6R4rEQI2XqRYYIrTEeG7zEeLZRjamfAoqEYnh7fegGeuJ0ncoceddFjePB\nk8vGsIDmlMLgHql7mIBHgX4lnbFldRZrKCBx7XPP+qOcMcqxdB/M46sndwnS\nOQF4hoYoHyF4OGC/UYFrPpQvLyJ+uDfKPF97rd/A36VtxRUwaSfXHMaXd5Te\ns3MTh0WIvxYQ638PY34zjT6HQccunReOt9By2GUynDV2yBjRJ80QLKIX8VTU\nUjXaGnRbc63H4vgOgwY7mtPTWbVMNYkJ5nXr704VK1kGTouLC2g6EPwqb+ZU\nu3f7r30MPeEGwZU+s5ff0jiYItIjr79Rj5BU9iluXymAom7lwp043706dbNT\n3SZebJjsUkoXamWcgRw4yQYLcNUbY3RIPQDyce+lJK2+K95Ha3fDwYOXtA9S\nd1y9Ak/Fkmll7iSIUNAYNpaWXbUdCoFm798iQ9PVry2Lwfx9TgQu8WOUha5n\nFT0qhHXVKoQ0WwCuufGS2xjJl6AuAsDfwCU09D11CZW4fCNj+z5WdUs2IA9+\nCGOaVSx/KU/uVWaNIdh/FrAns0QJwlcg0NJx5BLNLIA0tagV+nGH/OGs6nUH\nVbmP\r\n=7EFH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBdlOroWVCQRynQ+pxpWhmih+1NsXn0rkca7+SX/9TC7AiBqJgW3FPbXtFNportYumhP2hESMkPA2p9kd8WIf5I/nQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.31_1569526770490_0.2938433356825887"},"_hasShrinkwrap":false},"1.0.0-canary.36":{"name":"plumier","version":"1.0.0-canary.36","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.36+c9b8acb","@types/faker":"^4.1.5","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.1","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.36+c9b8acb","@plumier/mongoose":"1.0.0-canary.36+c9b8acb","@plumier/multipart":"1.0.0-canary.36+c9b8acb","@plumier/serve-static":"1.0.0-canary.36+c9b8acb","@plumier/social-login":"1.0.0-canary.36+c9b8acb","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.0.5","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c9b8acb40f0fb77c8319abfb7a9b0dadbdcdec7b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.36","_nodeVersion":"12.11.0","_npmVersion":"lerna/3.16.4/node@v12.11.0+x64 (linux)","dist":{"integrity":"sha512-cN7icXxl7mOP+Vgv1+MWoKFPchil4UeO/nXaaPiEoHg3Vij1VfKzAsWYcAth5sY0YObrjgVSRno4addNHbkpAg==","shasum":"c8735df2d04fbe2039c19f0b84e5bc10cc548d5a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.36.tgz","fileCount":9,"unpackedSize":18445,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdkHI1CRA9TVsSAnZWagAAUSAP/ArdK9odiVWuQLyKSM+i\nSzvZfYL/koE7mFfiVqgFuItsg1YgaNJ+4JAZz1OF5rgas1p5NkB5/zWdMLke\nyRqgjvvvcMmfUFTNazf6zyfEAh0sUZSBaK4fa1Oi6kzwMw7WVcLw+B3bpZVa\nNn01KKQPa7K0Q/6SM3TrgZFjEFcuFMi6nQmJ2qpnbQeykix2RdahXrTDxUGA\nC6vxppx4HaS9eE0cAe/0dhFRLYQYz2iEDOSLn2EDRKcZ7pXH3noOj4KMD8Wz\nUOXOYG1uN/VWOSykfkGfD3edN4fqknjpnu/UyQMkNhwf/+jmW+jF1uYFDroq\ndt7jyUJOycU/hCaDwyCxCJZL72ge6JAeYXoFAtwDRjefntX1yz1Q6wlLeLUf\nyloim8bhAqm58ukbEZF7ONBay0EhroZRyMebzxAOqRhU5v/Ig9LK0dXcsCIm\nSiSNsvhr4S0QJUTMGNolzJzQezrnfVtAEmovb6P2qBM77fg/9BxaiThGsfb9\nmHvpsdPxOe+/VLUxcKx0ZcWnrHRnICuzCJbqMyMPlzDI8NH4x5guJrsjYtYC\n/KY7SRWpuzGY+E7ez24ue8oC1tB/TRSMlfB1WyYe4jQF4E2mQMOPwylnPSs8\nWAoSiLhRZjHc/6seWR5exdfzfrn6LQPr//QA0tdYxUJtW3PMHU4/FiAOoVKw\nB2AL\r\n=JGTB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYEuvaMXJ53oFpw5g8iq5B38RId0m8LOIfeC0a6quKGwIgGmjGEJuisycvEEQmZRHPJfUVf01s0CB8UXUPw3J8asY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.36_1569747509141_0.7855594985408108"},"_hasShrinkwrap":false},"1.0.0-canary.37":{"name":"plumier","version":"1.0.0-canary.37","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.37+169633d","@types/faker":"^4.1.5","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.1","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.37+169633d","@plumier/mongoose":"1.0.0-canary.37+169633d","@plumier/multipart":"1.0.0-canary.37+169633d","@plumier/serve-static":"1.0.0-canary.37+169633d","@plumier/social-login":"1.0.0-canary.37+169633d","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.0.5","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"169633d26bcaf51677cba81ec1456e03b4c7dc4b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.37","_nodeVersion":"12.11.0","_npmVersion":"lerna/3.16.4/node@v12.11.0+x64 (linux)","dist":{"integrity":"sha512-23MKMG09cyAxGLEJ5f6wF//MIx9NyaB/cRRAKKaLeJuUBxNN1/gGbO2H3hR6WtWqLu0WFYLsqw5k+/TK48xUTg==","shasum":"7ab32d25651dbaf671208844a43275837a692ce9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.37.tgz","fileCount":9,"unpackedSize":18445,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdkbjUCRA9TVsSAnZWagAAZE4P/24OzpJrStA/GmPz/eeV\nd4ixLPgNMY97gr4NZRHVoaWAT05UajhHvzHxStAbGWJy3x2/SpphqfuO5m2y\nvJX2PtAQM9gqXWYRtNKMcH5jpZr727VV1lIRRUGCxLfCDj64CMGY/6iOwLar\nYKXOMw1KSejfvQTB+2Tf66841slaPvuyVfjRO3AzyCjv2WPc4zGr3CD5wHG4\nThLidMhQMIcjsLXAGAdOu5fGnl8FbSXQiQr/QRYVF1IulfNLWSvwh7b6ujW+\nZIgiwl963ATugBtmZNaQQU0nQdaoAlZ9Ey7bC1tTGqpfRuGiBqB/7eFFzmna\nQm1eYTOsFSvpvlhNnKIz1TXO965M0pz1qZfI+6EevrLqXcOKF8hV3cMaKDtH\nm00exSicylqnRYXOvXox0fsNfcxiR3HdDw1w3y51UZs9dFefKSK5J/z6jLJI\nVam0CyymsplDYkMtOG/M1fyI7DKmIz8PDzlYaV5EoMTae7+niCaIm4JkP/U0\nxcysw4fgD9I3/VCJ763BRLk5c/kop3Akm2JTtG68IgkyKmi/6qWjMBd2nOSI\nU6z/V9qzd1vgy2JfS9z1EFy/4xzNxfEYDa6XIZB2TJtYfNQ0ye9PhB0AGD/w\nTPbDSOoVDwVK9ugfAEqhxdSlHVdqe3r/epHIkaR63xJxkARhDLT1eM5ZsBNS\nwGae\r\n=vWyU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDENRl71GVNv+ZlAjIVpX560KzuWCQr7RSmzVv6Yi1KuwIhAOP7GKChQelN+uejxZBYCoCTZb2NQ+0ZDGsDbI0tyBHF"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.37_1569831124062_0.7513594765478984"},"_hasShrinkwrap":false},"1.0.0-canary.38":{"name":"plumier","version":"1.0.0-canary.38","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.38+cb74e6c","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.2","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.38+cb74e6c","@plumier/mongoose":"1.0.0-canary.38+cb74e6c","@plumier/multipart":"1.0.0-canary.38+cb74e6c","@plumier/serve-static":"1.0.0-canary.38+cb74e6c","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.2","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cb74e6ce0f5f96c34a8b3ce70670127a4e3c9f20","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.38","_nodeVersion":"12.11.1","_npmVersion":"lerna/3.16.4/node@v12.11.1+x64 (linux)","dist":{"integrity":"sha512-xGQVLe/9mBm3/wkoPlCGeHc3WKRzoYS3lWJJMsNQ6SvfnQrvfutWLqPURgICpplXMTKsPIGbB2KlYsuIFg1oUA==","shasum":"ad9195fb4e8f7d52841bee99cba3e46eff50ea4c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.38.tgz","fileCount":9,"unpackedSize":18437,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlx6WCRA9TVsSAnZWagAAovUP/jBEYFWSVWlxaWgqImnd\nemQlTEpFdmIs0d5F9TqmllSZlEYJ4d4zdYjrz+svkVWdZ/AFrs2NP9TPSLYZ\nxyXAqffFAHCRYXRBL0NYcVFWjk9ZSeafG6t4u8kpiQ7NJrRSM6uN4sCRvXvS\n3Dy/zb4QWKLrLXKowgAmvpBwJMEC4pwg77yXfGAJuLCqYPn3cGhy4w5EHfjY\nz5No+mF4a0eUrqZBUVmS8KgU/F98kgEVn0hlvbY+IC0//dbi5HhNYBU/N4t4\n44l36S6yASjMWmS/En6OaaP4opGg4FlYZOkR8hAAdOZqiuEAydaGx3olVZz4\n5CJFiH8atsImqu5ib1EKJCFqM/qNhg/x2hFkHJpoqk4/iIxvxN09w+T6hNy9\nlgFUPQGWFYV7hNTAi0l3TBGyqxralqhc+S7VNae0J4uuwE0tyxxwRxiow1JM\nCbwavUrCzjhPNSlcvmYp+jG3BCJLazt9ZNEcC+9BdUmPZphsuCdw1CHEEtzs\nTl9GsZd37N23691x00v6Qi8iR3PE2t8YwqSvMtF57zBN+UUMUqePzLWThnrt\npIiXVDxfdAmCROGBOB3RWRw6rbnvhZ4qn05IzssdXWEI65Je0MT/0M9gcZ27\nMeSDw2I/Cb5y68fZoGX2jfOmpTJ8Qh339Ru3dxJUIZp7jDluLZOxL6FjZjUC\nJNW+\r\n=KCM4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5dqZN++51eVfASvVg3tDCUn4mkADmSlN8FmdWMrYJpAIhAOH3/Ur3Be77kL98+OQijpERgdK5FUhWG2y+AGc8Z2TY"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.38_1570184853739_0.3386121877894932"},"_hasShrinkwrap":false},"1.0.0-canary.39":{"name":"plumier","version":"1.0.0-canary.39","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.39+83ee046","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.2","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.39+83ee046","@plumier/mongoose":"1.0.0-canary.39+83ee046","@plumier/multipart":"1.0.0-canary.39+83ee046","@plumier/serve-static":"1.0.0-canary.39+83ee046","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.2","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"83ee046a276537b5d7f22b2a60a9f7c4ab21889a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.39","_nodeVersion":"12.11.1","_npmVersion":"lerna/3.16.4/node@v12.11.1+x64 (linux)","dist":{"integrity":"sha512-aQdOV92vYDMV4VJ9Z4u1QOGofwJazAeflYOmjfJ8hgNacqRBYwVm3Z6UG1ujF/3u72PEmQXIE3OUJ5nVZG2PUQ==","shasum":"71c64a7ae48dc769471e6b1ad295df5d20fc447d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.39.tgz","fileCount":9,"unpackedSize":18437,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdmS9wCRA9TVsSAnZWagAAAMsP/2NVTzf8GrC4tZ6I5iOg\nqzeUkfMgZwY3zfCW3E7GghnslQ/Eomzu0gAGD8P3mZ6oVhEnw56CLoupi+tb\nMZG8KkyyxG6E3Ysd4qM8pMh6w74VUknXQhpyhF7IbwBhxEE8jV50Ibs9Wv77\nYKyWe/simhr5Xr/BorW98lNFCgb+XVdM91PCdtMX3Nn4sy4DC+1OCegONjVq\nEUlQzvMkQx3yn6c3K3BDCzT2BjA+xboK2G7Cl7K/9JdP7V0hDiHRMtnLojL5\nBoPOAdr7TgspVjjJfy+PIVJbFZ/feFY4NpnC8spVgeQrRlQzljuKIjGhcFtJ\nMlJNQQPDKoVG5fVlsGlhKHUB6CaQ0rE4Fiu/YjEf3Rkv3biukJwj8BNNHYri\ndHAftu97FNAZhJivS4REtzBX6Xt264v/k6N1BjuYnYrnRxJi8PhpjAyoWY/E\nB0UWRZXwKZvQArpQXFBU6B6m2UZpY2qSYOuSJIuYTInrgIoCZaoT3zeltkPK\nwKhTW9ZabjYK0KDciCT9RljSJM12FvvQcRfuT5Xx4uXHG0vnnnAtKDrF7zKH\nr7w+sUFxLchjXzBnXZyMVOS8PaaWAKW0Aroi9Q47Pt5HBMFoiapNdFsNYZ7H\nbDatc1pBO3UFUihKPT3lw3ydq9LfJATrBOGaM4+hndhmN5Z9Npbg6dkS48AT\nHZmS\r\n=b9Ul\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCP3IX9oV6I30xqN4P+wDADWTzSVjQU7mdL71skmARaYAIhAJCMBSA1wyl+l9UwXZ3DgeI/iIsR1dIgLfxawaBQapfP"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.39_1570320239574_0.07042738246226454"},"_hasShrinkwrap":false},"1.0.0-canary.40":{"name":"plumier","version":"1.0.0-canary.40","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.40+0344edd","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.2","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.40+0344edd","@plumier/mongoose":"1.0.0-canary.40+0344edd","@plumier/multipart":"1.0.0-canary.40+0344edd","@plumier/serve-static":"1.0.0-canary.40+0344edd","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.2","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0344edd674043421527e30d18509bb31fa8911c8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.40","_nodeVersion":"12.11.1","_npmVersion":"lerna/3.16.4/node@v12.11.1+x64 (linux)","dist":{"integrity":"sha512-27RUSI+AcAeDrRusL1fQp7DnBHI84YzmXCN0obXDdyOLzAqvxUxJCQWgOAHZB3G1/+0tx9TCBizDgIGEwALkDQ==","shasum":"a8b36edacfc56f37eb373909935910248889eda8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.40.tgz","fileCount":9,"unpackedSize":18437,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdmUBsCRA9TVsSAnZWagAAENgP/2bedRWoIfRmNHIe7F+Y\naRHO46qbOck1F6nwP7yic6FxBdBGq1FlCC7fDsHtFcbExYPIHGUZiQjNhvdZ\nRnLP/Z+SRHNjNy8Vc+MeBJZBBLdNpABwsVpX5heqdAsfmtDSB4rwru1eXhJA\n+2mUz6D+2+5UJNZYzm7dVkO56aTM7TCkOIteFCNgPwl0021/VN6WbKRjegau\nlwMk6WscepIROUkA2OwsmPKG8f5UyWLCts7/T5ccuw2ZANf/OOqLIDV7sT1M\nFNZLchIfHignWRUsz5MzHLV0bO9DJX7fnPP3GNtsl7bvTGb1LArMaN1/ngsn\ne4kgAiCXAs53fKRGF1cTxKCl4NqD/wNZFCEgqY63vrblUM0APsZSvZE+dnT8\nEihoBHfXQZDU3TSv1jd+HSNR6pSrPy4EE1OCvVvD7DBjJrNTFTu/hDr3mww5\nUReKz2IeFmD39DG2M6HfeYy5tX4xUFheu1z03LC6q4BQ9HguC1b70ssji4xc\nIxlGmRXfzalkZzF1Yf9Ye192Rf0aj//vgcpsUq8NY8WtYESSQWy8oJcSUdOH\nzWXMDkogtM+ckIxOGrBHrC593dqaI5AYhoBJVRnO1RZmTVpFAdYQeHIoUWXt\nWX7qBJ6iV2NheIcluhrRAZ+wXIWYiuZMTrt+nbKa8HOoxSH/oMQOgdzK8LPF\nUfws\r\n=dQZN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICbK+vgAi0qEDwOD+Rf+hG8r/sd2SPw4oczbIBleTCt2AiEAj+icljBoek2T63VynVWHN/ZovpWAaO/thc2Lrd4CXQA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.40_1570324587479_0.34093269495570855"},"_hasShrinkwrap":false},"1.0.0-canary.41":{"name":"plumier","version":"1.0.0-canary.41","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.41+fac0173","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.2","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.41+fac0173","@plumier/mongoose":"1.0.0-canary.41+fac0173","@plumier/multipart":"1.0.0-canary.41+fac0173","@plumier/serve-static":"1.0.0-canary.41+fac0173","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.2","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fac0173804a41548a42032d50104cd5e764e6384","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.41","_nodeVersion":"12.11.1","_npmVersion":"lerna/3.16.4/node@v12.11.1+x64 (linux)","dist":{"integrity":"sha512-4EsrYYMJGVFk7UL/TUIcc3Gmom0tWTMpThxHC86zRl6LCEV/vn0q8LitHD1DcTMquK4YZbjSJff+Mu7eKrfDXw==","shasum":"5b26df231c58a85821f45936eea1825205e2298d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.41.tgz","fileCount":9,"unpackedSize":18437,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdmroKCRA9TVsSAnZWagAAlC0QAJZrsvs2GfFOvYnkYPab\nOXujwaUwpUXErFPCFEM/uWEhljla2aBn6VMgqQnZRzMdhQ51CHA4rjC5E0N2\nigNpbv9V3DbWBtanzHe8aekIVoB87I0aXXHj8OfuWJx0HgQ7DVXvy6s2RUFp\nOcLs9iYgw35Pvxxg8fno8to94++n702h3Npln8MGUFXGIyNgW/TXz9XvS3gA\n4cCbPTLGYP6lBN984rlSLKT5XBBKpf/sAQQ7FwyiQsizDa3JFEj+msQBDAkA\nGpGQQvuugq7O5w5sMna58ZAayISLQMndZCM1LSv1Y2xb7L8UGvtRvbJTPreG\nYAgql6gc3qtz3YXSzjrWKOlv1dUVzlfgUYcoqebKC36oDYkHCkepOz/yxB2y\ny274edhG5TK1CeGViXxzzGRAN16bKHdsmflKI/FV/y/jPz1HuW/cCWppZ4IL\n8TmXqEADToCwmD1jDKF0w8uekaYroWo/8Ac1QgBBQz24WG35QBtnZVrlyt85\nI1vUrDl2IVMCgcYNDhobiS/JzCIxQoqDBNoifJmHcSKAWNb7+iQQGn0cm9ot\ntP7CyVM8vtkKsdOP+1d/Ccr3wi1der8pMUS0xpAMxwM+pMpObaat4GRsCeqJ\neuH4g4aNBy2EqDaWOnC/pWPEnPn45RvHR/lvl0GV6+7GQuDdwmNiUa1PbvHr\na1Im\r\n=pomH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICT6QSgwsb+xBQ6A9zkfMo2j2MvxlJ99NiPvLxUEoeDiAiEA6OABbawtw1S8nfZcnDF/c1AuypgjzAo4g7SV+kAaXHc="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.41_1570421257638_0.41187057850633746"},"_hasShrinkwrap":false},"1.0.0-canary.47":{"name":"plumier","version":"1.0.0-canary.47","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.47+d541574","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.2","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.47+d541574","@plumier/mongoose":"1.0.0-canary.47+d541574","@plumier/multipart":"1.0.0-canary.47+d541574","@plumier/serve-static":"1.0.0-canary.47+d541574","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.2","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d541574cfc876c924a198e259de33c51430b7312","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n\n## Motivation\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n![Benchmarks](https://plumierjs.com/docs/assets/benchmarks.png)\n\nThe benchmark project forked from Fastify benchmark project, you can test it your self [here](https://github.com/ktutnik/benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* Locate the `.js` version of the test file that will be run **(important)**\n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.47","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.2/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-iqJ+WFO7SDu6TkzjFq5QzT8opHWE2kf4gh4yOmzSMXQB0Bk2BAs78WtmfTEYr8P4N9q++oaLgU+lV2jYMmKzJQ==","shasum":"4752d374fad431a8364142529d641c191bb1b992","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.47.tgz","fileCount":9,"unpackedSize":18437,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrptkCRA9TVsSAnZWagAARIAQAIY7dExmYMskUhOod/vj\njD9n/Zx82VbnW/1quZ5AJPgbNUyxZMh/3e9CvfjU3KS5AA6XIU6znNn7xgLv\n1m2VnbEdau05GLEuAs2kflMHWqyV6TzIWK+88GtAdahTjVLw2B3rJKq2mDMf\nN3mgIfO7JPNf4dU5BrWPHEfXeJ97wLGj6WFIHE/tENf78urmWSPVv853+QtX\nsCZAIlNKo5IdNqHn68uGKclDaa++yqq5b5cyjd2mHynp4c4cKLClGjllAJJU\nFU0jD3TqkOhtjsbiJPQMXTyo5T2lqcpgolPOaUbxWgTYsOHzsQt8cL7clVMN\nJ4SXL746KkFJ4lwGisdND9dUGdgWPC6Epyh9/7HI8h7GfskDBLAQnPSmaZ8n\nCQ5EOD3Ns+6T+r2as8R55NaJgMiA/ArHOsfa69kRrWwNLBKmypYccj5iE1Ek\nJRXqkxgFMj4oB3zKNSoU6hOtTixih3oy3NAepeyGIOekoQqysl6LM7WotUGk\nPRy2S1BhdpboZmpIjCPcey04Oh4wGjJXrOsZjGLdJ36tV0whSuhr5mYWuKMX\ndtBKN7g68wWQK1CvKwuQgrj2rZBmh4mbOp6afWFt1/Y8FgVA1K1ELE8RFC0E\nreqXhpCUSD/loEpwd4fpB1b7VBFXt7aYJysuwBG9AIqOBJZxyyRONGGuRm54\nyVpU\r\n=TXem\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC6CHk/AmLhS1B+Gw6M88o5vFBpEli0MFDpRV0P2/My4wIgEH1gUJFL3QWWs2GYQEj16yIi/b+2Qg8Gt/YujDTUrmE="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.47_1571724132106_0.3107155645693578"},"_hasShrinkwrap":false},"1.0.0-canary.48":{"name":"plumier","version":"1.0.0-canary.48","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.48+cb31c64","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.8.2","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.48+cb31c64","@plumier/mongoose":"1.0.0-canary.48+cb31c64","@plumier/multipart":"1.0.0-canary.48+cb31c64","@plumier/serve-static":"1.0.0-canary.48+cb31c64","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.0","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.4","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.2","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cb31c64b134a29534637a56addb73cc74d1c705c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.48","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.2/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-Zlf3n4izG0j+xY4mEiV1yK1XTbKdqwRxzdKWPIL2Oyx1HXhISXp6PQQ3HLNxad1VHde0pwcO9rtvvyXm6WalDw==","shasum":"fe00a631005890b7c8092560493e015f46365cb2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.48.tgz","fileCount":9,"unpackedSize":20248,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrr7qCRA9TVsSAnZWagAAi0wQAJRyyNLmb4fn6q4ac3bf\nbpzmwkPBFUuwUo4rUrJNOu6B1bKUrrmFbOQwoCs0rE4/HodghKratWDUedzW\n5js5Tq3s1bFstPLmcV/vzuql3TgLGhoBGb7hqk0IfZjf1EFJRAgJmJlAS6v7\nt2sIvDPWazChi8MULVw94r2+8r88XcAMBcsmmqMVjJl65N4psKErwPEhz3b4\nvLecivlwUicIB8lJS10+oFgtyCp9m5I3SwMfaVCenOAe2Oqk/9gjUhJJO5hz\nm8dLayRGSflSKnsWkJh2jE0Uhxb3AAzsdyjK+czufrzjCBrIO/gvq5VS3HH8\n4u3DXWioHL0IKPa3HeHKXzZeOsZZYjOw4yMLHexYXKSr8ftOZJee31fTZ6IM\n94bXtoZfU2Mvey6UEX1i7RIp/rzYVUAf3/eaONmblhjRbsa+2izUPNtXZH7v\nsZKWjj1rTEmrksjRHRUo7R2OTVm9FB/j/Va3elcdKj9H52ksmI2G4lyOkJCb\n/ISSOazPxeHKbsjzb7ukvp4tFZKCkxMSWOMNpeJp0XKgvIdor+4K1WMw+EfL\nxLHnlbyc3B6Cnuv/vWWMA3/FspF/+VqlzcyCehuUNRO5Vsc1ONU4jagIXtt2\nuzit/cI4A3SdoGrn2iU/g4pRBLKU0SP3yKolOta2u8aQEeqEeiipYIWcjTCR\nECag\r\n=PJz7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFxzNjRKc4m8/KuVq0YKW7gyqCOdRWHv9uUvIhYleNBTAiEA3j8kKXXjhXaOL/jdY2uFN8ewAUz41zve1jA5IntQmfI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.48_1571733226197_0.644959658105118"},"_hasShrinkwrap":false},"1.0.0-canary.52":{"name":"plumier","version":"1.0.0-canary.52","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.52+afbda5e","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.10.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.52+afbda5e","@plumier/mongoose":"1.0.0-canary.52+afbda5e","@plumier/multipart":"1.0.0-canary.52+afbda5e","@plumier/serve-static":"1.0.0-canary.52+afbda5e","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"afbda5ea45ca287b7ff7cc08e9972a6586c44c2a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.52","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.18.3/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-QVeTWqyloXyLMbkpxsD/SmIb6KMefw7OLxeRytAtVinNvr2Ar7dhu9mtPN+cVeUMqtASgAwqZH/DOaoxXivzqQ==","shasum":"8032a642213e58fbb592bbf844574708bf31a4fb","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.52.tgz","fileCount":9,"unpackedSize":20250,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsh4sCRA9TVsSAnZWagAA5wAP/0DakhBTvdha/RL0eY1Z\nUn3msI0+jkyngUxrAecTDzbELHuh/QE/8hGb2BkNJEzw/5MXsq8ZjoaBT/Xu\nrSIz2TYuxgyVHElXuaG5d1PISsMa6y5cMi1ZIIhI8zEgzCdELZOpbXUDdzBp\nV56Yiz6hyrqILG2+rUZyeTMHpVoq1ddDcJeapuwWv45FKh0Sv45YZ9hRi/sE\nKd4tftkyqb8q3xxuBSOejUo+JIp1DH4F/kM4JxTHyGw2JKmNM6APCwRHSc5a\nK7skq3xJ2a43DaYtVhXHocHidv+UofP7hnNnq+qqXZLqCDAnNqWcKTpBKCOw\nGANjDRQFFy94qPbYoXjP5Nsy1e7lYVK6Di19zOddyBdJc3gByM4dY0K9wvhv\nbgVSOb7SzuYanyvAZhInXuu9pkAghadBuQx91pS/Rn0Avdd9Ea7U+WNoG8Kp\nVSgh65DQ2w/2wr+NSei+rbvqY/X2h241TfKiuTS87F7TYuMpU/mg9pRD84cr\n7J2mtVV/+bWdwafmUHbs21EewTX1PXWf4c4vJyoUe1/FJdr6JMt3sLDT3Zvj\n0p5JPq+LVVW+L/7PR+XamyBew+nXUcSVcTVLKUG/nVzERqFulzadRNQDbFpY\n5WHKls+NOTVHE28DwYmL5eyqiyvquJXu4sZ4tnz9OxiJR4G8PXAom7hnF/cE\nthRS\r\n=h0UM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGH1D3psun1+eJalWTM6vP1ORrs0OYLZ/ClQblM619HHAiEA8IMUTU23u0c9b6l0u9HIxEusS4AWEayd8CFVe070m7E="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.52_1571954220144_0.6035935246233697"},"_hasShrinkwrap":false},"1.0.0-beta.10":{"name":"plumier","version":"1.0.0-beta.10","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.10","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.10.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-beta.10","@plumier/mongoose":"1.0.0-beta.10","@plumier/multipart":"1.0.0-beta.10","@plumier/serve-static":"1.0.0-beta.10","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1ab6337771fbe7949a45f45102dd24924c631e7b","_id":"plumier@1.0.0-beta.10","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.18.3/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-EVqvlUfGt4d3oYnlQa4g1Ltj4i8rAaKjzjXZnSeuUL7nJaM/2+rreiegzzjnw2bhQ99pS6AoemLJyr3cRwb5vA==","shasum":"49c48b0effbdf9c6cfb7aa78c5db7dc6ef29ffc0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.10.tgz","fileCount":9,"unpackedSize":20190,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdtMTWCRA9TVsSAnZWagAAdCEQAKPD6TpUhNBpJxT1eMRC\nhVjjeXvrA7rYCqwvfCdZW+Fet4I3Is0egoS4X/S3AcOJKqj40dLPf9xtRI25\nyVfJzFwuYKqc2QtRQjikNYmC5bPgcWbXM8OSpgUlgLkteYT3ixBhxpo8KMJx\nqgt/Bqcqm0S3tHoQuUlAzhGrDsukwR69lqJdLzp1Y42cW/yQmHD2FGHVbKXX\nvWqLVnolVoWbb//XhXClpd5KoluV2eq5meFy5O+Wm/xeMmFQ+dc/B1dtwgwW\nWhZJwb2w4yjnIAQAdaOS8X/Ek1NUEAyZw8BGn1YYW80/IqYevMNJpTqVUas5\nI6t8HGWBL1zY2jqoM5+5xQq22y3tvWYPnnn5yF0xWdwhrxkaivOtxv/AOlBi\nGkqKCkOJD64qz7lSu/CCx++tqy7XnOqh3ZFKdEz8eweyMGldSaM5llLL2Kkz\nkaR5+izVikvwPkLlyKoFDMPL/AIYUnH+AxE3m5VTaOwh61r/9ln695/tIrTx\nHjI+Bn2myq4mCDi8EIiobTqI7U0uTwnfJeLALvn9FWe6JFjFYM8WkvCRjS2B\nJe2ec0JzJUB5xRNoNVTxMVrvx6Ue1zYu0sC761psY1EYgXHHMy/IRU8rg0oW\nLwe1wUBnkdMEj919JD+KXINrtdLeO8kDx3IDEDAbCPpIkhz7yhfA4zfDWWUr\nUb8e\r\n=f8Fs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDTaPlPzC1kYIF4wklFjDS1I0UCd/Y5t2Vo5oqFR8Pg7AIhAI1wei23u+BhwGjmfcWMVHHrXIX1WB84CueCYxTTOfXC"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.10_1572127958496_0.0964963613724219"},"_hasShrinkwrap":false},"1.0.0-canary.3":{"name":"plumier","version":"1.0.0-canary.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary.3+4337618","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.10.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-canary.3+4337618","@plumier/mongoose":"1.0.0-canary.3+4337618","@plumier/multipart":"1.0.0-canary.3+4337618","@plumier/serve-static":"1.0.0-canary.3+4337618","@plumier/social-login":"1.0.0-canary.31","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"43376182a3be0704f41c0fa50ace7966de17bc22","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.3","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-vBpyE9gfH4bAvJSJJhaugfQBtSOE3HE2OgfCP6Xu+KtGR1rD61CYAh3tcyBS4ULLE67soVkrpHch2Gt4LHu1Tg==","shasum":"53ea5e7808ab0b592725092a8c382e653b661d1d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.3.tgz","fileCount":9,"unpackedSize":20244,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdtP9OCRA9TVsSAnZWagAAR3YQAI8U62GJjNUDnfErj1Bx\nuNRP/OPfeTN04zkBIplCINUZDnLLnxwlj2kmRq335er3EalfqfxCgXOgrD2O\nxNayGtdkTR0Tqg8imenSJ2IqmgTSduyDqdwD1OFPZQdafao7PCTMd/nwWr5O\nYsn9WI8REoVK1ovf8qIybLLO3sSZUSmNJSlK4qvYya4DIffir09ePLfSSqVb\nUxSK1aKIcvc9jToUFQwQ4Ss+jWJecfwKxYopW4WlFsQyS89RUKCVP2odeSaj\nu6tU+4TYfUGtIEgxvaaDAvRHDoHJ6KYJcIhxghVOMG7mBZerb/F3uc5dJQm4\nixSOH1Gy/gGaowOQN+FG10VuvQjknnj21p91gUj/bOLN4As1xtqs1K6kdrE8\nUc2neyusoL8HLaiF3A0Et6RMOKadUh2hY72VgbbE0eUQF9lXSZ9n3kxegmhB\nWXSpfKkotJBxTtp+5zllfCcOPU04nPhZ9vhKFzjiWqem61gFNHWMcxCNfRsV\nxVoZh+tpYSSq3HRxj8Uvw+Kyf6ZDuFrC0S240ywayt2DxU/b2SF0fUTnLc0N\nxilVGgY29yDHOrjmDvder6DtSor10KgNbxtARj58zc6IQ/9R7ZKdr2AZvSeT\nYJDWTiNZAgn0Fmngxy3FpevsVp3MxPj/uc0NXNfu7cPZowoV6tQFRakcfroB\nD8la\r\n=sCYH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDNLAGMilIVucY/dh0psf545mRjW+gFktsgjg5b+AXi7AiEA6TQHcnpz45OCqy+Va4hYWhFbZlOc71TCWsnjkID5VA0="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.3_1572142924784_0.289312161784431"},"_hasShrinkwrap":false},"1.0.0-dev.5":{"name":"plumier","version":"1.0.0-dev.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.5+20ba598","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.10.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.5+20ba598","@plumier/mongoose":"1.0.0-dev.5+20ba598","@plumier/multipart":"1.0.0-dev.5+20ba598","@plumier/serve-static":"1.0.0-dev.5+20ba598","@plumier/social-login":"1.0.0-dev.5+20ba598","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"20ba5985f655f939a7e126cf10a2a9c4afa0db57","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.5","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-yKuqGVkoY0Jwn0z8mnwMoOLFJN2l1IdWgllUvCrW2I+IvHH9d8iUcmJTsyszQ3E9ZjTOQ67KmLpGraBJKqklyQ==","shasum":"b0b1212315918d5365338047450ac9df08ab6395","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.5.tgz","fileCount":9,"unpackedSize":20230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdthOvCRA9TVsSAnZWagAArOsP/R361uhXsau2ohXQBEog\nUscfLADjZt36x80t1UMAEb9Q+jM/lIFT8AGD14J4k1Odew3xiXRD5FNwMF5n\nzmwsFoRhx1m+7kNEjCv86dbEcPuYV+ToI4L7nwAb1J0IREURiD6y2MVhfiqb\nQdqIXI2RMhBLp5l4zAlcQXEGlWwchgkv4pSHh3NiHGOcWGfwZ//Y3+CsjLZS\nksbqi56lDPs0Ru/43M1qJFMPs+Pxr6hwnvmMc4uFYuAaC20VEGAw5amc8RLD\nhpbDfy6cY6aNqeVNib8QW4vmfG5PhkfBO1jFEfc6Y1qH4p1NfPhvdamL49wr\nQtm+QlsHqwFMcAM9MICoyHmE9oEeJWqrd9SnqHZbCBmIZ60eclBIlIoQGZmI\n/+oPqlgFmCSRp1C7c58cNrBXY/aKLPD1C/Pyb/MxphmDA027m1oRWrlAaqC6\nEu7sNFhBKlqyRssfEkPyMuBp0H0tYsu+HItKdEgytjeI/LHJCU55gP8EUj41\nX0JgW8hX9k9ptMqIJPaoJ+vP218nNzaXUlHLXwa0H4fxANbpfj9M8nLuWeLg\nRShKQjUmuhdzmoNgPqNrrhmw0tjwrlWqJIm3zzVdrlfIa1frpW2kJJpW9sQI\n84cuSevfqO25u/9HqjWJpMY6J1KbdsSoyuSdCH2b2u7E20GTwdDwAT64iOl+\ny+GB\r\n=wG36\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDH8QP9GQHTJZcsw6zOQchwaFtegmdg5FciCqvhf/PBXgIhAOqvDij6oNQFkk4IOH7pP+eCow4uA9oe779+wMenFBBr"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.5_1572213679180_0.6105676803464444"},"_hasShrinkwrap":false},"1.0.0-dev.6":{"name":"plumier","version":"1.0.0-dev.6","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.6+41e078a","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.10.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.6+41e078a","@plumier/mongoose":"1.0.0-dev.6+41e078a","@plumier/multipart":"1.0.0-dev.6+41e078a","@plumier/serve-static":"1.0.0-dev.6+41e078a","@plumier/social-login":"1.0.0-dev.6+41e078a","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"41e078ad402188f145611f346c98b8722da171dd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.6","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-pMeh1T3zTxIw1lRY3c88mQXGigu1IZiikeGGNbi85TbX56LPXAi8M0Z5+GFGUxR9GECVtw3mvvaFB6WojHDP9g==","shasum":"5382cadeb10ec32da3e9de387e441a2c8aea4b06","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.6.tgz","fileCount":9,"unpackedSize":20230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdt8nICRA9TVsSAnZWagAA2qkP/3oMNz/A+jdvjqhQ9SXS\n9Us+g01lokbi2F+N1rffUnALnJ6Jtb97U4/uQ8HY46X5gdkROMouiQSwQDh/\nbHQUkWI8ts/+Y+wkSPMdQ2Xu3/GNR6fZRThji6wdi8ohNQ0HJarGw+1Y4DWs\nFxS6SD3rowvPQNLJdavWuhDpQS1754ITKRRsqj3a7ADb5/F+BfE5O7bfWdSL\nyi3VpZv435dHGU2KNq8+peRhewOqw7W0yblSMQrZwEu65vhLj4FY3icIfI/Q\nTLmd6/gRZOLOXMtvmK3AvnaiIv1IDcv4oBt4cLLKNh2hE/dSg/ogR9V/sBpK\nx3ArJAiskaRFxlNDYPwhFxrSbk0RYtfWH0WH4n5rHLC0HtFhLUWkkT/rm1yO\n7awPuFJfeUtbRYec04+xAS15sMnC/5W9XjKQ97WPiXTs/gj8EncpXCM17EOF\n5VOYqLHxOPsZvPibyCRe9yIUp5ePLNih1Lv80ThHtGiKM5cOIpxk7p+XClB0\na9mVAdpiBXACSUcLQrCqtVZne5enhxrz7jQzZRZ8nJjsPbb5TfoOMRBD6Sto\nHzANUS8S1kLr/Amp3CLw1OvsODltbAs0jPfiuxB/p/skUXnCVdw1X0eU1lB+\nwb+5ioJdvmwDXpjY0252Bqfq2Sq+oYJ1R9d5zqSwaUwgdy6Ak8+uUfR/JrIJ\nZKv6\r\n=W4dr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHGuSTfdaE4gNPpbLyrm8juH8mqmAoJ/0oiam9358rxwIgUTg4HVAaRUrGIdhuiv/n+lVLvddUoN1PEN9loQO4BJ4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.6_1572325832587_0.2515136616445621"},"_hasShrinkwrap":false},"1.0.0-dev.7":{"name":"plumier","version":"1.0.0-dev.7","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.7+21ea29c","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.10.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.7+21ea29c","@plumier/mongoose":"1.0.0-dev.7+21ea29c","@plumier/multipart":"1.0.0-dev.7+21ea29c","@plumier/serve-static":"1.0.0-dev.7+21ea29c","@plumier/social-login":"1.0.0-dev.7+21ea29c","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"21ea29c73d0625e921be39472e0b2669f93538f9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.7","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-JgbwySaWSi+DBtSL5wNdPy0WvP70ALdjmTiAPX9wW78ba3MZSKTKvhpC0AUVwyxZd7FF2uAn00iQqjL39yFSRg==","shasum":"8e8ac1c4539fbeac4d55640b63a1f339ed5fa795","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.7.tgz","fileCount":9,"unpackedSize":20230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdui39CRA9TVsSAnZWagAAcY8P/1OMnaGcQMPLGcn0VnSY\nlWLe3L12+ts3sDe0I9zb1MRqNoRn3oS7kSoJxp5qkcuWe5exh05Hiz+gP5ad\nW4eQcI7mgje9wP4Yd+C4XjVl3DTLWT6p43k8yXftsm4qwSq8fFnHqcWwlkKJ\nkbUPT4N64J+DKXbvaxbiZCTviWpaIExsAzsm1nAvuuKqeQRblFVKFhYOrkIT\nw7olaAGoILD00uhQ5K5DCMVADOhw1JL2P2PF1HjAF1NlCLJLFxzZuihge3/u\nWaD/sNvRjjpAzF65FXaeaU31YBOmjls2nnrBvkROpjeD0vAvLCYS5KEhwQiy\nYi0ySa1FHqPTZ6JzsmAe/1NVOIT/am7hkf1AEUEQ2TS5WfsG2YJmRcL6DcUK\nepsY1Df7V4YFNoqzYBvjdDAibC0BtJC+Ncy18INN0LxJa+VZUtQKKfXRgICU\nG+QePm5YjF0muHL++BMfWZHNLdLfz9tsvtGzfao+JxlHrjJ4Lx+OjX8sIbw+\n4m/n3T7eqZ95iEX5rzmKe78peNb4wQbXbFMU0W/HRHDuafyEt0g1veaduoQS\nsbG3OOiuiuSusz0Ih4sZUvqd8lsPk8oFeSqrAA+t2DVrpmNKfeGI509RD+L9\nqSYETs5vmX+SFxd5zUAEVCiimurlbLpsfI6aBE590SbLLz2us//QYn0gNoKn\nIgZi\r\n=iaBE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDFNzFN9l+B8MRK5tqu70wDdHoB0AKVrv5flLVm38QhgAiEAiBrCKQyBHoJsGGgNZVskEckAY3DO9ZGHRmHxO1T+pfM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.7_1572482557036_0.5051827641069773"},"_hasShrinkwrap":false},"1.0.0-dev.8":{"name":"plumier","version":"1.0.0-dev.8","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.8+987dd39","@types/faker":"^4.1.6","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.10.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.8+987dd39","@plumier/mongoose":"1.0.0-dev.8+987dd39","@plumier/multipart":"1.0.0-dev.8+987dd39","@plumier/serve-static":"1.0.0-dev.8+987dd39","@plumier/social-login":"1.0.0-dev.8+987dd39","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.2","@types/supertest":"^2.0.8","autocannon":"^4.1.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"987dd39c6c92b07be0a6150a8c14408fed3c853f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.8","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-bYNZJn72lsMNdl3Et5TnCJoYQH0dHKnyA6rKbZLEjMgILvGXG2NE0iOzqxpOYKo8DsQwg/gwiiBnV4zA8OQuYg==","shasum":"9cbc4e1b6a453cae343d76e1f9f1e86d4fea7b5a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.8.tgz","fileCount":9,"unpackedSize":20230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdu9MNCRA9TVsSAnZWagAA0H4P/i+g3DQC4a0HtVLC5Lcf\nhMLp8xb1BINEnVuostPcN1wSIMsYwLJpFNzWRwA9V3oEDKZLlAQ52jdNtnkw\nLGn+gFnjD/23+7KILT8IVtq5aM/G4nQofJ1/zAlUun8AhDjHBykFYU/J3vhl\nBzRoqsoat0/BiFJKy/d1UmAj+jzJPq/WBxOi/gQKQegvW/a1lXQ4Yn10sOEp\nAPojXFlEi33mtBcxT8rqVgYZVvEGzmorNZzmhaNsFK7weJcL4ANt7H3bpSQB\njOSx9OW79w8r6CEOhuwRmqkNCpNgZDHec2SRleyWZg3rhKyRfm1a2J1aD+Si\na10OTQjmmp/O9D+p2eCC8cN8pYjzMG1MEGBX/OEgPfSrqHlnd5hFX6AM2cx7\nuP81x4vIGwwKEVcB3qNdsNCvDQ3RDTQFl0BaZ5iIUitXV8sLu5vK2ke72e58\ngaNYD1HEoctoY0O4mHjEFI3Fu3nnbphA73Z+nrGxB4xiOglOb9iwjcNN2aEs\nvkzkoC9eeK8eFCofsl0v72qLKF+0/797CugnXwEq8UUHjjj6Fkt4djgPekvd\np1gdSQnztLMrDLwbKxILpKfT0G3RC+fUUN6HKwPLiaWUyOnEUuXjqU50CXaP\nUQncYfVq6br8EQsL/srI/CP/mbyNUaYHD0/nfw11KV/6xr9fbfAN4Q7toycj\n2sRb\r\n=wFGm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDeD9/g7BvAsqG30v3dqvPjbOvIxhyZC3lMYH/nuHqELwIhAKkDML5uSZ8jqZlic2RGiOq4X5iCvElP3dSgLoF1ljKO"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.8_1572590348473_0.26837235443953067"},"_hasShrinkwrap":false},"1.0.0-dev.9":{"name":"plumier","version":"1.0.0-dev.9","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.9+b4e2ee0","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.9+b4e2ee0","@plumier/mongoose":"1.0.0-dev.9+b4e2ee0","@plumier/multipart":"1.0.0-dev.9+b4e2ee0","@plumier/serve-static":"1.0.0-dev.9+b4e2ee0","@plumier/social-login":"1.0.0-dev.9+b4e2ee0","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.1","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.3","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.0.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.2.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b4e2ee0044a0758ff0be5746ca24c6640afddee0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.9","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-P21FE2x+GUS9juHoVeeVHLoIftk2MNUhMTktG04oVPiQm4dcGk2twz6jaAH4cyfPzPFySVK0V+IG3FFK8FjfDg==","shasum":"eeb0d3787131b330c12b423600f07be584aa6c88","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.9.tgz","fileCount":9,"unpackedSize":20230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvAYqCRA9TVsSAnZWagAAmUsQAJ9K2LkzAssS9eOWXIjM\nGnvdbz912MdAx20XP9lrz7d/DoFKg14qAJHbdVEeO7peuQiuJnooBV5VQxzR\n0wS11odRTX6xARxozqDYROiggZCacuG1MjaqslP/KHrsj9fNnM9ES4v+Z9a4\ngM79++ffGTMap1NdD54M77/FtWDT4CoJxyHrhdzBTsUsiFq+j6Uc+gpjZm2z\nHWsfF6dh0ASaab7AaryQBIH3wx4oedH3M32isa0k4TD2xZmnxModY+HO9Wcf\nNOTut9uiG2QNnbvrMjgsTfoMiJaWpj1Tx1hAIs+GMHH2F28qhUR/ryEf6jD8\nbn4XEnN6VOOFVEaiSvdgORYDtRQJtCzMBF+2X8KvpuZb0uyVMElwAoYXaEuM\nJWJcR9hOg+tRO3nJNKeWX62NGs7KHDATVYykXyNarOzYC7bkkuBDfM/phyks\nbgS3f8DKBZJx4QaSgLQxFhhNFfO2XAUg1brtQzfBin/rrKYUClucankuriPM\nmn0YmqFqVX1U5RWbv7i7mRNiGlj7JIojFpmvC3JUcb+K0znsbf4AWtLG2Jo4\n1d1nfbGHRgB/7u3aYb4PxH7lgPRxiRI+yUeFlgJfaHHTQvKlqSRf/XAHKf/n\nnI/HL4MF3Wim/kEDzXcMIS/em/yrMaOgjUlg9NyU6TSYoRb3xkAUPnEsHNcB\ngqjM\r\n=OiDv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCArDhGskChy6goe58bK5Sm0CyqF+XowAXy09wQe+3GyQIhAJXBQeOovAI53SMFU6YnvCjN1p9svXRfwv25a1Zaf8R6"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.9_1572603433683_0.5146864159737858"},"_hasShrinkwrap":false},"1.0.0-dev.10":{"name":"plumier","version":"1.0.0-dev.10","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.10+68fea27","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.10+68fea27","@plumier/mongoose":"1.0.0-dev.10+68fea27","@plumier/multipart":"1.0.0-dev.10+68fea27","@plumier/serve-static":"1.0.0-dev.10+68fea27","@plumier/social-login":"1.0.0-dev.10+68fea27","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"68fea27d0b31b8443477b02181e72c080bfa9c10","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.10","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-wDj/XmrYOFcdsBz8TKYW2DgdCwncLFGZtKayiETdk1n3PdbCPRcDDWDqfYieUCg+jnuo2aYYlYzgvbDkHqOXgA==","shasum":"f1c279c91cb6e006c2e6815d87f9ece3d014b50a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.10.tgz","fileCount":9,"unpackedSize":20237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvhFgCRA9TVsSAnZWagAAdAwP/R/poD68yWLbdUcpOtxe\ngVu+r5c2zSNPswYv4+DXNLf8NXX3jF1eMEn1mtECMB0jrsUwOdHt7Q/HQ32w\nc0nlQN89aIxBsX71eg7h/zOp6OwKg2xXLOth6IRVrjfO0sjnIYxq4hs+6GiC\ngHqzdT/VxnomdaNNVJPWhU3E5TSi0LnY9AsBDhPaU7OxIeS2HYhPmhOPSwPT\nd/zzPrSdLNDjhRrJf7YimD56tOD2L5t9y1WkC61Ntlx2aB9EMA61xIuGbbN2\nYL4G62VNbd663jI4w/Tb3Wroy8GlV7qZ6gEt2SYIAniMCcCJe8BqrYMvbETx\nvH8CRUFZgpb3+26IWY5EQD+5N751VQTtmUEiFyvr4NiH3iqHOKy4NTbx4q7y\nAg93Mh2JCxumm3o28C+QBpvfZOint142rQzbImD1PgSLu7JQA/z13LjB2gX9\nFwtsB3OJbLfro5xDv3RwcspBpElUk56JSWbgl/e3f3q1jTIEye3j38gZRSf7\nRoAWu0JvtI3/7J2DBBAJGeMwIlwsAEc6bQsmx7IgRGCFagYIuzn8b214zg85\nwqScufirMpsR4X03S+LxvydM8E3wMs1Jn6qCniwWOzW07bgQMPoUj+/7un5D\nkXZFTLoJvGmZPiogIAT4Ot25Rb06qfpcMqzhqTeA9er0s1HaYA/tBKE1fmkK\nbVTl\r\n=stSd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDDtmAZTKjVUJckBF/EdjF++18fxSK4PpT2UwAI8FEkDAiEAyjrGLild3xVrXVl6Ad3zdwq6IK0AaqrIfnAdHYyQ5+I="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.10_1572737376139_0.6235521213151143"},"_hasShrinkwrap":false},"1.0.0-dev.11":{"name":"plumier","version":"1.0.0-dev.11","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.11+fdf6d25","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.11+fdf6d25","@plumier/mongoose":"1.0.0-dev.11+fdf6d25","@plumier/multipart":"1.0.0-dev.11+fdf6d25","@plumier/serve-static":"1.0.0-dev.11+fdf6d25","@plumier/social-login":"1.0.0-dev.11+fdf6d25","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fdf6d25f535d150f13a6e44960debe31e4305d9b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.11","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-usp1dPf5NmKzrUpv2U6uwEnSkN9qpzhlAm81cTW3w6hiG15V6ZzchmpyLjXC1GxOqCXXQED5+cN1jBbTaIwZrg==","shasum":"afb31617fd9100e4906fc0cdad12ab06bc3eb2dd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.11.tgz","fileCount":9,"unpackedSize":20237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdv7rNCRA9TVsSAnZWagAAn8EQAIZJWsc9KPq9+uyjrdXL\nJn4XdYi2/WggdecsCnjRbWQTDMWOdNecXp5WD4BiMp86XMjOSd3OXYgU9r4x\nKdVskkO4y6oTBL5cadQVh15wAG7sCucZ1BKikI0uB3P9KDADF+LaiGSeQpQV\nb2QNNV38xhC/pzU1IFezHdz0RWiF0zP0zeAPMqlrPk9RqnzfvdhL8OwRpzVA\nznKfPjQGz92V6GK/khmWuuwRQjMvWs6kDvoiZM+MsIheCHwVZcnLeME8yzQ9\nD+OfGFnnvEHBCcQianE7cpwloj+bid//gfGoCmLZbncyjRLIvrxUgBVzKxs/\nq1IdPFDaayK6suiJa3wJlSaxEY9551TSHOf8UJtHzeqY3BZDVbPUNIS3Rh3E\nuYGT1s2l+oH2WMiZL3VsHp+kG7+uQ52OzN5W1svIUpLll3KVo6x91vD5EdVk\nOoztfFOGJAGfooe9xfRe+V/1StJWIxh12Z8h5B5BiBIdXEgLEboCwZ3R55Sh\nnheVpnmVLIM4w27P9Qk7aRzWOpubMjHFc0sUp+1+spLSVvlQulu4sw2LYAeP\nODBdDOT8xVDBotyjnzPklhrbQdasBwbVsLOWW0HaMfsD8EcVKGVqqnIlz/gK\nVEwc05pJ6ATEyDBuegDRNTjoEMTt0dTw1+fybkLLRe1CIO8DjqxSM7EQmCJr\njNhH\r\n=LZ0V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH1meGfHjOqPU61eKnrX3jCst9fZ1b/+6+DF55b8+DDhAiEA6RefWu+242atkR3M2lMpIiH4MzqLNrvArHcjSox2KVM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.11_1572846284734_0.9793534833949082"},"_hasShrinkwrap":false},"1.0.0-dev.12":{"name":"plumier","version":"1.0.0-dev.12","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.12+6f37e07","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.12+6f37e07","@plumier/mongoose":"1.0.0-dev.12+6f37e07","@plumier/multipart":"1.0.0-dev.12+6f37e07","@plumier/serve-static":"1.0.0-dev.12+6f37e07","@plumier/social-login":"1.0.0-dev.12+6f37e07","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6f37e078872709538da5392f1da91fbc61995edd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.12","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-D7aAD5feHym/YU7TEBK8e8PuswTH5jNBVScOAdddhBXiAOXOou54EaXw0/AkPcOnQCL/Bkt50n6NEArzejCcpA==","shasum":"f66385b4a86249f1134a5f8a7c524345eedb1710","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.12.tgz","fileCount":9,"unpackedSize":20237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdv9bNCRA9TVsSAnZWagAA8A0P/iDrMvIZVmrk6cqG4M0A\nentCy93KO6nexD0DD2E1+O7Kg8X/EaFXv0J+15cDb/Xaw4lEqbMNjat30ZpJ\nYLvwIUdmB/dMHlV+UMhPpIp1axUSAvx0TqNsjpzujY1dYKkJhukeG5AUhNEL\nS6y17ufMroW+ADwZOd1ZjSoaEDO9SZ40KQIhgzo4dhsegWiVlXZ+iL04VEu7\ncqAZQ0HfcnXFh2nhgpkzJRjN4irv9ZSX/5q0DksuQe9aIZpf0M4FWMrrtB5l\nqox65x+xx+r5cXKcIv4u4Exv1kt+Z03tZG1DkEDfoPPluOjgctPPUk21OUrA\nxpXOtHcXC9KGwH9QVfRivKHklTpDKavGj1QK6VvxBDCghFqpWWQBU35LWyXh\nPxEUUW+PTcdf0g3moJR0EVUw9nqS12zdToXRjmy+mbKk2VsKFbyOWXEpFzov\neL5s9nNv6fHvvlh9D9x1Prrd47Nt+vokfkXhMQdLYDu0zy+8AltDBZj+1nrZ\nlTul0ILxW9IIIeFvhMknCgPZOuUVXMsT1400P0HolLIGG8jBXlQOwqJHwjgl\n1RyuPtH4gPHzbm2wxMBLW1O77pcp/IwZEksTMwMPVjTgeZEOj80+gPBMN8QL\npVQfRBrDvdyUDUvdnJgwOhO9NetcctlnoDrL+sclqGZ/GEEPz5jBK6wrFLMa\nGQIT\r\n=SO6y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3thoATPw53vlrFp0C+UGHPCTOQ868kX0ORggFd6tB9QIgDn48GefL83lIXyw2K+RIVZNXl1HNE7W5cbS+1E4croU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.12_1572853452547_0.13100725565854443"},"_hasShrinkwrap":false},"1.0.0-dev.13":{"name":"plumier","version":"1.0.0-dev.13","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.13+ba03f5f","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.13+ba03f5f","@plumier/mongoose":"1.0.0-dev.13+ba03f5f","@plumier/multipart":"1.0.0-dev.13+ba03f5f","@plumier/serve-static":"1.0.0-dev.13+ba03f5f","@plumier/social-login":"1.0.0-dev.13+ba03f5f","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ba03f5f598ff989a65398510159ff319ecca9cbd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.13","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-itnYipaJL4CgUzqW0SMM+qH8aTxkTSGSmCnObabIOzJFyQM+ASXEoqgpJpjPRbdlJymFkL7NAVVSwZSppwReBw==","shasum":"60a6d0a0f7d67e3b0dee57916a5b537385e6fbfe","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.13.tgz","fileCount":9,"unpackedSize":20237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdwjYnCRA9TVsSAnZWagAA1E0P/iszhextVvksq8xLP3Yl\npBA/oWnNpDudWa6m2+Ud74N2Ggs9SlCvdgJpiyTIh8bKO2f3W4T2atnq6y0S\nblEKYZkaCZth89roS+2eV05WiTb5nHmaJcsJkp7dUtOaIKR5dLq8bbzmM9tl\ncNwhd/8fgSv/qeBYNpdoCt4QkqrendOobvdPt7sZXlsnc5Vvru1U4FA6X6cU\nxJn4yeUP+T0r38Caj0B2OCbFHfehNFq+THTnhuiJ9NTULJi/Kgp2Ll7i7yxz\ntzoUEcbOYU54Po1y9XWSWtmHayrySOIv5LfbvZuYACRaWuubFardaYvJxDq5\nKtIuIbxWyI+noluOImLrloqEOssDpy8fA+haK6xUm6ssoZsVafb+eWjibHfM\nmNVGI/Haul50dCfYFWderaa6kVnOmzCTOJU80rK308Q7be7mImA+G0LbOYtS\nO+B84PUVZXU5oC/Uzn1UA7CCDiqWIxgaISRTyesuk92L3lVW24fqPq3pyepd\n8AbJ6G/UoiR8HaY8+XU7wDsXdWWp+AEtpQAmHMQ/jMFjFOxjs7g//Lnwv8nt\nOMS0ymR4lbHp6JCB3aBTK6QrSAH6AMN6a8SSeDDXK12zI/O9PW9XvS7sAaRT\n/CdvBgJ+2pqv0mrH1EFzFdiL/yWplD8f1czPhC//0Xz76T33xNXF0PA067gj\nplr8\r\n=G2nt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFlarkx4e9+xrmUNSTcVW6mzXMrkwI9TdFpX/OUgaMTVAiAfWUpCwE7zkbjs5fDqkjY7LZVwBqUrQdMKBH5FtSeHTA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.13_1573008934772_0.47290557391819044"},"_hasShrinkwrap":false},"1.0.0-dev.14":{"name":"plumier","version":"1.0.0-dev.14","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.14+39f91e9","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.14+39f91e9","@plumier/mongoose":"1.0.0-dev.14+39f91e9","@plumier/multipart":"1.0.0-dev.14+39f91e9","@plumier/serve-static":"1.0.0-dev.14+39f91e9","@plumier/social-login":"1.0.0-dev.14+39f91e9","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"39f91e901a896446144a8ae7d6ef99593cf44cba","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.14","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-gXSjXQA6E3b4WpsmdUvxrOPEQz4JVOF/CjQ1CRrLEW8xJr86iKJBRhY8HAnINQbkL271S7pyyo1fxCjlXgyfWw==","shasum":"cd3421fb3912c84b8e20db0796755287ed859211","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.14.tgz","fileCount":9,"unpackedSize":20237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdw0qUCRA9TVsSAnZWagAAjJIP/2uXM5ecXO74ikoNvL/p\nONHyu4vPLbzMJH5x1LGXralGYe4ochvCnN6xVp8FpbY/NnrFByemLdN/03C1\nsGWN+yxHiOYEoKvMkX3y+pzJsjtH/8HkkPx/ltY+dm3l18xjGWL5gDhTsXm5\np4CEJv+kigzT/X9eqaC7zNzbeT2EBwIy0WPNox2g8LgsYsukcU6ssUKVAstw\nCcitUAdSvQHqYhOsx6hKO8YqutboqTe2Qoxr2qNf2Oh/FZ0+uY4CltqSXsv9\n04066I4EqpQc8iU/IgpEoIrZbxIsammygPMSREnk45uuyUedVl8XO4AnDX+5\n1/qS4sHhkyvbG7YwtWFjnWSb2651VUWDjRORevWpru9c9of6Y/YV5EXmd94Z\nuWO1/XikvgcmDEg8ur5ZT2yrU4JZBxj/6aZdd5H+LulgRK6J0DHaO11vgucs\nefNmY+5mVPePYpfYAQ3Bbp82hMat4jRRyxmnpgfVPsdraiQXYfuZB9gzKZ2u\nEMAsd7a1AnBsEKUZR9X3/gK/rIJdcPXVaYjb568CC7NS/UdZ25uXqq/rw2yj\nU6btqjXCHzmICaRaLcEIYMopVF5n1zsgcnCdFSKNG3+mmzQFLJWZiv/6G3Yf\nBjMiRy7zWx3uZjkdA6fke7Wn6V44lt7H4vGVe8xyNONC1I1+70nIfgIdMN2T\nGPe3\r\n=RthZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEuWawq1H2WWGuEfwQFAs/zD9M6vTELfxnY7Tg6G03VxAiEAh3YH0RobDxacsETQMRHO/174H6mfra/il9kBMFBvmCg="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.14_1573079700344_0.46893922167866875"},"_hasShrinkwrap":false},"1.0.0-dev.15":{"name":"plumier","version":"1.0.0-dev.15","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.15+d2d1238","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.15+d2d1238","@plumier/mongoose":"1.0.0-dev.15+d2d1238","@plumier/multipart":"1.0.0-dev.15+d2d1238","@plumier/serve-static":"1.0.0-dev.15+d2d1238","@plumier/social-login":"1.0.0-dev.15+d2d1238","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d2d123809c2b2cecdf3024fe2e87de489cb3cba3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.15","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-hQ3o8aBTmrh5YDXPPIMfSWqCwDTQ8bZ5F0L8ecMsXC/ZD6rERqsEdMS3dc1uDYpNWx00w2bbFSCqzvbIbKIxOA==","shasum":"e066751fd30b43fbcbd0d8bfbdad90fab41e51cf","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.15.tgz","fileCount":9,"unpackedSize":20260,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxtJkCRA9TVsSAnZWagAA1F8P/3NH/PlK2P2M7nxOdNPD\n3u6bEEMvCinMtVJ8VA3x+pkWB+M3mFvkdXgCu/QD13RlMF0XGho26FuXVKa0\nZVLMAPaPXKpvxd7eOS4mCHLB2EThdiQAo9+0+SqqAPDzf1lF/fO9YtYE70AJ\ng47gxTb7SkZtSnf+NxlcqqtGKjWqve+i+shKs1LeH0iLWvLJX9kChbTTvuWj\nfURPrgumj2gtzOmkMecmul2lNN0U0v6ESn63etC48zmbQb/bBwo3ZvU+lxCG\nIx3jl747diQbBkfcvC5YdjHsu/O94W4Zeq4cVqyiCGzQSpT6Hp7SoeZIEFWr\ndgBRAafq0gPTqvGqhgg+zetFDK1uBXCB/R0yhobGxyUhU5N7tEiKdVycByTT\nlHRuERouVGImsXzsH0PZK8CJNkVzbGGZgtqP/eK7otY6J7QF45s540zCyHxz\njfOb0XtHfNpSiyr5Nfd3eMCAc7rEpXuA68c0p7D/7gjZFGFta5sm+Uctg9l5\nLzCciw+TsvTRyLFVpiNjwCR4ZL3G+V/x2KaQtw7cpOWsOaMlNmyNgJdbkb/e\nl1pTMdduFcyRu/C8Yi061L4nb+ZDDE2TDjCDgiyT8zrFBiezMx3McPx22I71\np4529o5WWWl+JinJYnFv3mGsrXN7FZJlkIN4WPueJsUNGr4s+4pvVolYEeCz\nvXC7\r\n=mkKY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICBAlM0EKQ8Zj8wiCDzxTTIca7lMvlhQMVQxYQLS9DmcAiACxGzUSrj0rn70SAJWMb9bsOKtLHcIF+uwhISru1d0Cg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.15_1573311076019_0.5833069302050311"},"_hasShrinkwrap":false},"1.0.0-dev.16":{"name":"plumier","version":"1.0.0-dev.16","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.16+ceb0f65","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.16+ceb0f65","@plumier/mongoose":"1.0.0-dev.16+ceb0f65","@plumier/multipart":"1.0.0-dev.16+ceb0f65","@plumier/serve-static":"1.0.0-dev.16+ceb0f65","@plumier/social-login":"1.0.0-dev.16+ceb0f65","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ceb0f654c29fbf216513c3c60626511492ff56af","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.16","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-9XH0jMks83v1x2qXYk/r3bB5Qaa3ymDtaLsISETpn75d8ch/oTCEmPX6vPcAGupGn0xDOQE/MZ0RTL+t+aFW4w==","shasum":"74e03f6bac02f0283e9afa3e6c18b4ff4f8f5e50","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.16.tgz","fileCount":9,"unpackedSize":20260,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxu2hCRA9TVsSAnZWagAArNsP/0zg/MgSLnUyD3LNAJRI\nrKD27yCvOPF6lATkH8hqVyVGCW+XGdx4cIhI1UUvfPnjsJu1roUDrmH/rRYW\nRsRFg486rfQCtzbKentkkZ0NB832o6zZSoosrvFOkOwwIGaP9R4SUS9xKkHh\nM11JpTrl+wXNDm6CiJNxOufDJ/Em0VyYNSdeq+/Wp3DU/JnfOQ5J0d78QKEl\nYIQqvB3nELqCRKFQoSSN0zBcTTWoAiyunV3fPoZ0myj2nZTo/9lM7NDEU8Gm\nHNpELVuLfgx2vHPJJdpKbKW5ososY9jdGZewLmHOni7j6cfap5kauGzTKbUf\nh/h0F4Kwq4PwfIH2+ebfMdYKc+ye5FXkfmpk1yNwDkMnkl3T0qdOTVXur/dG\n8W6Px+M7SXhwCrMP35Vee/ATaZIuTnqdlqMBoMLk65I3DD80q3fGrSyFJfpC\nNpwRHh4Y+JiQQfROLvqn9z+wDrTE3OVQc2q//j34bzoiBCZE2ito2aHmprI7\nZ6jpTACPbOD86zoidGwGoCVqkb42Dn9fPCW+Xv2hJ32gAhONOHlbpmHnaDFU\nF2n9RI2vD3McPlnYDiNZBoZk9tUveKCBDippj4vA6SshYmIrQB6yVP3h321o\nvv/YncEUmfWyJCjTlRvA/uIRihCuHuB0S6umhcFTFK/TJdVdwCJDeaqm8Icc\nNBfn\r\n=f/qu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBIJ2F3oFAt0gyLHoPZIuk5hV1Cj4liF2s11NHvGADH0AiEAoLdfEGyqoybQ/6wOw9vYl9wrUNB4m0m3PTvp4FDq4NA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.16_1573318048834_0.45555215122996806"},"_hasShrinkwrap":false},"1.0.0-dev.17":{"name":"plumier","version":"1.0.0-dev.17","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.17+ab4fd91","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.17+ab4fd91","@plumier/mongoose":"1.0.0-dev.17+ab4fd91","@plumier/multipart":"1.0.0-dev.17+ab4fd91","@plumier/serve-static":"1.0.0-dev.17+ab4fd91","@plumier/social-login":"1.0.0-dev.17+ab4fd91","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ab4fd91080ec4f2268241e6ff244e2c3353c1e07","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.17","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-4L5SrO99w2AJlfb2fESh3aqay0L5m+W/PHoLNIhkO6O29wz2VVjYeWS6RZhviWrkLTXXBuVGB3rD6hbl5swx7Q==","shasum":"8846346bcf771c31d5843bc5eca73229742acb9f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.17.tgz","fileCount":9,"unpackedSize":20134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdx+ypCRA9TVsSAnZWagAAEzQP/RrWjSOOYQO9iOM6s1hl\n2BDoG6DF/HJBKs3NlFwN4Cf1NPJLpXp1TSHIrRrqmnKVnaDdgNKyLba+lEVS\nAXCjIahA1+M2rdOnsQQ6S0LByUZINj2DlvdxOokFstiRaVHSWTKfOSSXIpKm\n2PKE5Xa22KXSIfBpCmFz+Ij3RM+uWK0cWCYAEq5Ap9ky3c8gsiJ45080F1UG\n4AXPQcD6GLLDa/qKSPQQlhm9DP9Ol1ad5RL/0SwYpTJfv0T41jrZHadtu4uI\nKuMLV4suIRVx0vbchplp7tqUyniavcwMdPUfZ+CIWW/9GaARQqgDZc8QC977\nbSDeGRcpl0PYhvEUAeb5MVqILhgD2NiE5z4aFR8sAOwGhJXx4UKbQkZMtMj1\ndU2VWgt4KrN/Nrm20bkPTvzAfYKsniiVCm71/Ls9IeZ9hJu9e0d+P6g0/C8r\nVYLT9so86m9qVSb7qkcyrDmAoT19QH1T+t65VJUIvQEqYdTDeMXw5FKn8OP2\ne9UJ/Q33wLEooTHlaZ7ozB9zycqg3ffNHRxDtTnxrDn2exuGNN3TmBtG2PlJ\nMm0bCjzyUNXfEF2qNcXRULIzAimiTtR4XCub3J8o13f1Eu1pp7L9cVoHJc0G\njFUC4nzIPL/d8xhT6l0/xIIZ6/o47zRdTRmFmssrxeCD9zvKwoiinuOl2NxP\n7Oj+\r\n=aq6s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA1r9dzllSv4AmDyFwst2x/Aza3CquKHbBhk1Tx62VQnAiBnvWDpvSFQZEIm2SaAVwdIGSr6oo/EIUEOqiCH3eofZw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.17_1573383337189_0.928054446855187"},"_hasShrinkwrap":false},"1.0.0-dev.18":{"name":"plumier","version":"1.0.0-dev.18","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.18+9e9e2cc","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.18+9e9e2cc","@plumier/mongoose":"1.0.0-dev.18+9e9e2cc","@plumier/multipart":"1.0.0-dev.18+9e9e2cc","@plumier/serve-static":"1.0.0-dev.18+9e9e2cc","@plumier/social-login":"1.0.0-dev.18+9e9e2cc","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9e9e2cc3b8f12572f1b314770bb6cce1d7b409aa","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.18","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-fLgUIkSg6dn2ozfrjHQHvJYMr8P5rb6Rbc+Z+g66wLLBWIGwfWTXbGEJnY65EPAc+sk9/FkLVDiWwFmFsXuFBw==","shasum":"7ad269416416616f5ad8b551945e62820341264f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.18.tgz","fileCount":9,"unpackedSize":20134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyIxzCRA9TVsSAnZWagAA7kcQAJ/7zDPPv/HmuxmrHZL2\n0HvJKRqZ4CjHbk9qf5QDJl7gmdD+JtRYC5FJ3eYOcxZHgWQq5xlsrlB08U+h\nlPwvY+F5n0lbo9BWuYVPPf/SH/ymZNZmaIxZrRLn3QL2iUYX3Fdl6wRSmQB1\npb38kkXjrslXPgcUKEyTIbTckNlAwiPfqoatcJwkVeWYKk1JpXZinGlut1ie\ntWTBPaSyqM2CBlIBnif42W5fv/vzXDnbScNhbZWmG1jrPKM9VQzVXTbHP1EA\nixwKAM9fXBQUDScU6E8PcdYom/lNwQ8gmhxent/104b4vn+m6xDxCB9hGDJ7\nMubBsV1GY1pZvIl/Qv1RjK89KCBjaWAquik1vKS2eem8GeQOj4JeNsl3M/Pl\nahhGol5vvECmT6Vvy07ph8Qci6rEXrdfmf/Kd5Eva95S/aSv92a77ADmwFb7\nzDTiK3Aattqn3QEhGprjwaQXrD3gjJMjij0f3mrW49U4GldFdXcxGGqYhxQ/\n2dBMRi/inNn3+fFDIMOkVJeHuZMGw1EtjXSSbemqUrJi3W4/inAe2laIpcRX\n0O9ztTPgoekyZHPhrpRuwi8WgmFHHL7zkUdGokt1yfqxtkSIQUym3YqjbRHF\nInIztqtB8hWfT7DEDtxjkQ+TFo/dde2ywNvRvwZ/5rRDrGLRRstK2Z9tjpF3\ndOfH\r\n=mWjc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZv/ahReA2VeKKwjSvKkV6oXOlTxjAS1DgXUbSlZ84/gIgMokM/l+t9Bch2BiJlLO2lfy7kfkSh9eESMq3v8X6FDM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.18_1573424243300_0.39660462017286147"},"_hasShrinkwrap":false},"1.0.0-dev.19":{"name":"plumier","version":"1.0.0-dev.19","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.19+0f91f0f","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.19+0f91f0f","@plumier/mongoose":"1.0.0-dev.19+0f91f0f","@plumier/multipart":"1.0.0-dev.19+0f91f0f","@plumier/serve-static":"1.0.0-dev.19+0f91f0f","@plumier/social-login":"1.0.0-dev.19+0f91f0f","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0f91f0fd3fd36ff8f92883d1c7bbbefeefab2867","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.19","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-oVuPB/DEVUJO6MEwfza+R0W/O7hdmEYQ6/XZMS2MOrz+P7fO51drSx60tkeJS2UWVBvtvv6PlQG+FN9dyAGAXA==","shasum":"11ff9dafc457e9ea2d7f1c66549a954c99e4fb1b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.19.tgz","fileCount":9,"unpackedSize":20134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyKRPCRA9TVsSAnZWagAAUCAP/RC8L+6tXlI8mpBgPg6J\n4mu6HqTBUSMDPz/+XIoIa0hhq3BxCCZgfU2uISaq/HCyYXYPW9LiEU8qjgua\nstroTatdgb0Hz4/EVITdC5D0CEBHfydj6fW/nRgHhvfOmlIFpSDReOkxY4wx\njqkXrNM2IwR2v9JdmN/ttz39eYjzQEZQNtBkC7jNIEzYVYz+TeAatj7YuzrT\n3qhkuif9nhgK11lNXnTS3X56I5U+4zrih16avmE0fVkvS4bmrDXNeXdLUl4s\nLmouqThiYebTpH4/rtWzbg2EQeQl7vebETsMG9hxC4FgC6XN1fl2h0mKiBsY\n56sdOz1F7yypEm8lsryjrXwJ+HB742Ttst7LGRjAtF6gVOpO7kGUwZp2DGfb\nKfjrMUBg5666iTxkSN5QDukfVAWi4LCc/qs0GYRih+IfHw4nMkrb6k5Eobtf\nNSCd/0eFSWFo/x1yio7hbxhoQUpPi89sYx+BFjA3Avb2UY8ncrsGvEkEWmtI\nlnXZQsCfJl5Jjj4VZQ4dV9G5AbRrZifqv0/fAKvz81c5mjXLGUMqOIX0X1Ed\nI0HZAqtqwj9Jqufai7SdCQpRsQMuLLI4TKJeqTab0ewDbBgXOS3JGXjvrwbJ\nzeUs2KH2ay166XjIp4shr565+POecrNPUs3J9eMjynoM9A0oTnoh9u9feCMi\nBUB6\r\n=wnyw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHloZtfzwaGXyvdnF7R2VOZse8zoy2PL8uKMaGt32Qs4AiAhJsR/zetqeuA7nFDKZ7lLhc3OR6uSOQsS4VGdg6ql7A=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.19_1573430351593_0.7977842616422861"},"_hasShrinkwrap":false},"1.0.0-dev.20":{"name":"plumier","version":"1.0.0-dev.20","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.20+8964c69","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.20+8964c69","@plumier/mongoose":"1.0.0-dev.20+8964c69","@plumier/multipart":"1.0.0-dev.20+8964c69","@plumier/serve-static":"1.0.0-dev.20+8964c69","@plumier/social-login":"1.0.0-dev.20+8964c69","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8964c695f7edca9862aa8ae7bbbc27c30e9cde22","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.20","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-WjCH2vR9r7kP+8LGWVXuQeBXfbmZYBQ5voXEMwswroruUs253nOoCAKjYoGcVOzIQlOkBtN1RhkC4YnK8k/npA==","shasum":"fa6b8f5598db7f7ed1569aa0eca143f34de13d4f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.20.tgz","fileCount":9,"unpackedSize":20134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyKdLCRA9TVsSAnZWagAAJCQQAJViB317p6biHqJxfFsG\nMTxFURewGOvdEsmoVzxsg7ljkwCHMt0gWYKNOWobXQnSHt3SL1RwhIlU/68J\n2AnqHXweZGE/8SmljrLGlNx1nYdH3WhFOdgc2WoTtxwc439AM8QfOILf/T+B\neRiUKqv9BUp/XQ3zSXt9bU16qe1LThrf1zk62XD/+3xnAgub8iWPR3k8YZkV\nXAKZjAL+k+WMO4E7KXn+Ra9B1Qi98BoC6dscbKoJDFCekigx9bApS3cPBttT\nWo9I0twCRn/vdw2zDsWcQpB8tnqcRKktZJfLt3QkvvnbwV3227PFcG+PHwEm\nB9k59J+JUl3A5OfbyCakoYVYQpWjkLCyPbUT9nfyNy8b6aEfqQtCa4saFv9E\nvMJ0sP9lA1LIquGP082bJAPI8sjKZTU4v8BjQv9IcPw5d+IRy9kwKrt6VF+e\njWmAId0aAvRIASwwMrTd/ZnPm8WKN2947epmbKNtINxBcIAKvGwR+4mIdYpc\nfu4AuEGxIsrUaXtUzMBhBsBpGxC7KeCCbwtrjPF1dzAxFsOiqzQOxm38lMrl\n6FltwDhMfhM9feQJxsvT+vUW6pdBCzGIoH0XtKccpV07c1RZcFwD+Fw3aQrf\nCQG/oOX6Ly5EVecvLZzd3iFCr8/VkMG3NFmy4QM+4K9o4HqwatH76oTP64LQ\n+eXX\r\n=vfIW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMRVxFWwwGdYwt26YX1Jq/shviNL8NsY7XOVFsDf/YqQIhAMH8WBwwgyqQTMEYIXLQnPjcV3745q0Lax2IxM7Qqs6+"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.20_1573431114868_0.2548928915197526"},"_hasShrinkwrap":false},"1.0.0-dev.21":{"name":"plumier","version":"1.0.0-dev.21","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.21+94f88c6","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.21+94f88c6","@plumier/mongoose":"1.0.0-dev.21+94f88c6","@plumier/multipart":"1.0.0-dev.21+94f88c6","@plumier/serve-static":"1.0.0-dev.21+94f88c6","@plumier/social-login":"1.0.0-dev.21+94f88c6","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"94f88c64bd5fdcd7f8cf7037d0c6498014f1c365","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.21","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-bOM9QrMANu4nD5Sw3CiIKE6FYSz9gZMCOnFCUIbFypNApdFLFNWs893TY1F1TweGICVGpq18PElFUp8R0yD8kA==","shasum":"2ef736b6ff9617543272fb3d25aff858a4e3856a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.21.tgz","fileCount":9,"unpackedSize":20134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyK9LCRA9TVsSAnZWagAA2wIQAJ6ODQ6iecOgxITiUKRZ\nNXczVJ95s+rnzWbZuUXQg1nhf/YbbJBTuJyY2xXJzbB5C562nxWr5TDPKf5M\nPcM1dSPcARb8VUTJRqtHnSG/B4WipILTaLxMx9KsdBGTIIPyW2nKV6wvF+su\naePS9ablbL4wWXrp/Iz/RUu0rYRZWBglKUVJoIpGYtj5MZAwtXtgMAkvJtxM\nx40uaRbJvlN0OCE9n7M/5IAyRybNTzqUsfybYTM3qAUU9LLwWcHPPLEaIlmx\nJpv3Jlh2tzFrr5DGq9c1nbnCEwpnBzj9NHoo7InHKUpP12eEcjPXErMEZz8O\nOUWksIE2wmdczspwx+iE/xs3d3Xui/C4uus+vlM5R2lV6xrVGRCiCS/5mhzj\nxe5sJMGkIIao+RkWn6D9emK1ggbCH8TjoCsOF0iVIVXS+F8rgEDNdza+uTvy\nXCZlFtjVyNixvJXkZJCXh/zbJSBbs7lon7fd7T/NQMXmGDaImI8Zd263lCjn\nAge3dJe/XdByqoDuxDLxUmlz//nURzpfP1IgqJ/8RjO6d88ox666vecCXJd4\n3NpQhsuF7gNkW0pzYFj1csVozCbFvRNpRlPQ5rT03rQrrs/3we/IpF5xsRbr\nKyt4Sv26RXINyosz66TLT0+kS4gtwL14lIb5BVEIG5WpDRizw7b/Al2rmRxz\nbQrJ\r\n=Qt/1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC9c1HhKtE9axDb0Aey0jffnImfwfnwn3CLPlldIIgMDQIgJIVg7WF7QTHcYV5GUoGnDOCRhg1mIPu449m+gWCwUps="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.21_1573433163082_0.29685006755039733"},"_hasShrinkwrap":false},"1.0.0-dev.22":{"name":"plumier","version":"1.0.0-dev.22","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.22+c4d5734","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.22+c4d5734","@plumier/mongoose":"1.0.0-dev.22+c4d5734","@plumier/multipart":"1.0.0-dev.22+c4d5734","@plumier/serve-static":"1.0.0-dev.22+c4d5734","@plumier/social-login":"1.0.0-dev.22+c4d5734","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c4d5734e3162215752fe00c7c722ec499505de7c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.22","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-NBsdAE/vMO0vxXRp/SQoDp5xVirKlfcYuK9f39U+f3IdTay8uf3RqZBX7VfnPV7navuWhAE6xvo1v1jDMK5L1w==","shasum":"2869647f1da932b6a2abbf317a03c517f18c194b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.22.tgz","fileCount":9,"unpackedSize":20134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyL43CRA9TVsSAnZWagAARWcP/R5VDTI7Es+I6sLFU2Lp\nHf1bdFwcKKc7Phzgd50RfHok80EPWW+DNwtk1INJ2WuZjHhUnERA3HpvG5FI\n387UYrd9rXHSpTVZ5UhxJIlEzrjGmH2MbTEN2nfxcLrOnm7ZPMaOjdcyHQ8S\n3NNJiGdHRfzGoghyxp1yV7jnhUZj7hUV19AcTIzM30GUvSKxsL1/yG/Xb3wW\nHjuzs8zzs9E0C71dp6YpAnnssyDGFfB951s4DAf98nHC9N4/GGRg4btGMK8l\nyHE9IIYhVKjWZmZ/rqliUMcvrm4rwXcc4G5FYrVzG9a8PzDXMkJnEFTqH9we\nQ6AWuR5R24gYuPXh2TD1XfHeteAlHe/n9EEsUNupbUCyjAlJj0z/usdcTFdr\nhssA5WPjWlk6KfEe4/LU2D6Unv04XrWgihXXvN2GShc43/bmIhZBF0lNEht4\nfKjQu4jL5qsk1iJ9C33N77VxlpHHNfQMXMCwBW2omqYHCP+3FBxt97F0oy0+\nP12+txW9pTtuxI5ojoTbowIpQhVKqSh1yk703SfRClJKUg1fPgGXvHlomBKb\npSnLHnj6DupXKE1IqZnE88A72I72ooMNNWdf5eG6OakcfAJlcQEY8ycJhPHt\nnLLnXOlrFkpBJ3/vzSDEGzMO1Z6+4ZNTl1HcCHj6CGUigRXUJ5tDKizI9xM2\nsKNI\r\n=hCip\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC4wQZxdCNBclYpDaBFOPYBTGN4B6YhTDNLEsquro3e8wIhAMbNLrpZu80lB2GT510+53Tn2Pol82GFbZIPDj2bxkfB"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.22_1573436982943_0.13566949871386602"},"_hasShrinkwrap":false},"1.0.0-dev.23":{"name":"plumier","version":"1.0.0-dev.23","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.23+7d8ce37","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.23+7d8ce37","@plumier/mongoose":"1.0.0-dev.23+7d8ce37","@plumier/multipart":"1.0.0-dev.23+7d8ce37","@plumier/serve-static":"1.0.0-dev.23+7d8ce37","@plumier/social-login":"1.0.0-dev.23+7d8ce37","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7d8ce37bb404c0e509d7e4500e4b97f1acdddb67","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.23","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-UJBej3/gor7QHNPPHz5FOMkPo6QdEho9tyDzENMd7zj3owSvw2PBMHiBVL6k7rcfmbn3xXIyeU2FPzckYySJcQ==","shasum":"13cf4428048a894520a7f2622589ebc99bc25434","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.23.tgz","fileCount":9,"unpackedSize":20134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyRiNCRA9TVsSAnZWagAADQIP/0yy5kLbu7ouF1qURtIN\nbVqOkHopruEtJzuwB2t56oC6hTM4EXIuia4SYPgdjOv0Pl7zVuBwio+Vc8J1\n7i9V1y2j0vQreAz3H+kByxQdt0OGbElSVJViE0dr63sNEBc5Abj1pqqNSt5y\nBRJ7+f1SbV4qWizGjOZ1T8ovexHjWfqnAfuUIkMdSbmZH0yOANLyvW5lJZuE\nsc7fsnbigsez5XZsTYXORJrigFfu1ZTfy82ESyiSG3WDXqUMhG82YGle03ep\n7FWTH+ox4bhjoqeRts2CP6bPxNIAPqQXNGOssIJRFpfC/k/q5nvl6WpQeK8D\nZoXLsRllvNiera/m2DOAlYKsuY0qM+9uxgegJCDIk68lBLl8bK6yn8n+lqVK\nVQ/vATkYnKOZ2B4WvsdzELb6O+knSt7250eWtT5SW8d9+AJtcAh2GXui5G3T\nFqhHJlIrOJXhOW/eRayGR9bmC0sw0sz27RnVonSuNlNccEvdBDYg8bInDPnR\n01ATpKeSSgVagJzfSYnn49NmeMVF5U35iF9lrxMq7MKByPXWrRusLVvIcB3f\nFPxPBeCyV8lrDYs16KqSTd/1PTArvhvFESd1xZEhQstz/JkJf8uM5wLLM7Nt\n8gMo3nLLLBtodVLLpOyRb+ZqpPQRrjSxwre9sCb6VbVs+HoCV4xmeEiHIY/0\nGqD0\r\n=voZN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRTF/Pm8hBCKPanjK1XTiK1DrfFBm0PFZDh5X/1w6J0wIgVOYvzUFyAKuShyetYa5zX0mqVcyRDppnUlbgS3F/JAY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.23_1573460108756_0.7751814177029588"},"_hasShrinkwrap":false},"1.0.0-beta.11":{"name":"plumier","version":"1.0.0-beta.11","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-beta.11","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-beta.11","@plumier/mongoose":"1.0.0-beta.11","@plumier/multipart":"1.0.0-beta.11","@plumier/serve-static":"1.0.0-beta.11","@plumier/social-login":"1.0.0-beta.11","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e8027135a2f8f935cafbab713d2df8f7ba62ea7e","_id":"plumier@1.0.0-beta.11","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.18.3/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-ROtvRxshKeq3iEDYBub5dBFR4wul6eBwxIUsiyIkEPWDJCPUxRwy0uBO17Pm5FD3CQfJNuFVC2Ir4TMOfDR7gw==","shasum":"1d7ce39b6024ebc5f5f400bea9dd170c9b2f5551","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-beta.11.tgz","fileCount":9,"unpackedSize":20085,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyjRLCRA9TVsSAnZWagAAsGIP/24UhVQ1WiBt1x6dpB2d\nko0M/O3/xKVP1+Fv55Ghnk+lHcP4jcjURG1qmqAFUxMgmfaueHtXA73pB9Jo\n6rMUe7iyHBe3wSzELiiZYWMOamg78csG7ZHyin07Zar19DvkimlxbnMNUcOY\nrzYyjyzlf8coPIKK0BCOkAhiSZPU85ozRyHILip6JbmEXhtlitWph4M4g0rJ\nbiLhR/BLZV65g/z3iQ/x4at1D/I8oZZfGHuLVOEp4J+BMN9I9qGNtgjK1dtn\njrvGnyJ2dula/xaoIeLZa2UJ9/LbmZ8GsZXKxenDj74GWikj1ZauU6n97XwJ\n7zyY3J+ugpVfIoe1L5Qq4T4wlF84vD9ojDUHWjwbLbl6DJjAXRoeXwHGaQip\nQZdlw831nkOhC9AvoqRXGu6nHDdj7faN5v8Nk2i+Nh6Z3OmqFiV7Qp5mfdNn\n6/hi52KcHa804xprmuhM6vN+2uWpSMA0CvPBRgm4gg6NTEXoQHndgjc873ei\nX4okFPw/q9x5HlUnZmjPzxwORGnTrDus0re0YOx5vhSYKTaOH9eDyxA+fqHM\nLjcXFJtQKqkLEIPCis3DdfgVRbNXjAuCdYtaqgSX2k6vVOfmpaIuHPXBj8RM\nZXBU+wH4SYQFMLGvqwhpkCtkT1KQZz0yzhHfQcNf7eMkYnPI1TUq4BM+Ekow\nSv5R\r\n=b1zy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEYxO5Hs1gCEmp+b7ET6QL86ZlguLJN5kkd+BqFugNHlAiBVPJMD5zwQpGGrYdDRbr1byxl/nGL+Ycv6QzF63hZQWg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-beta.11_1573532746647_0.2130407680438251"},"_hasShrinkwrap":false},"1.0.0-dev.0":{"name":"plumier","version":"1.0.0-dev.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev.0+e802713","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev.0+e802713","@plumier/mongoose":"1.0.0-dev.0+e802713","@plumier/multipart":"1.0.0-dev.0+e802713","@plumier/serve-static":"1.0.0-dev.0+e802713","@plumier/social-login":"1.0.0-dev.0+e802713","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e8027135a2f8f935cafbab713d2df8f7ba62ea7e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev.0","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-cwYGYw/2x82IvCVNCUyj2tTu81GJz4IB/6JD2vIm2rWajSmhwf6YZu6ngfKrGT3oJ+j9v0nK5rk4Sq/c0U84lg==","shasum":"79c03993682e34ff4ea0d8bf093a9dfde6968c91","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev.0.tgz","fileCount":9,"unpackedSize":20127,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyjURCRA9TVsSAnZWagAAuwcP/1Hdw3piJNx62Fr8xShC\nDNxP/B6s7Ed0wk6e7juuB2ZjLcrB6k8KK3C4XwwUl6A9nBPv72MBbh9Mh8hl\nhQMDtFW61KwKAyz2DVr/pIJLDGwLIUTizTBhVsJhTzKT6cFgACGpppoXp9Uq\nE4hFHWDRi7gZG70VGtBufxPGy3razFps1+uG0KCtcqsPJz8xPrdoTvzHIp17\nDx617/ZX4vRG0HQ1y3mONkvUcBNeQ80LuI3mlorMSE0YsRXmYAB1RoBf6wug\n/1p6V8yBgiPw1kBEhA2nh8VY2OluHELQI87qEsphiF0k4vBGmh7ye8VQtACE\nYuYV8nXyfooN+5Uyu82fki/+DYNpimoE7R63yeP3CMQOK+2zMjkCtVHFCw+j\n/5bN57BsgLBbBFNn0uUfS6tvSI4/1+UH5aBkaf2yWUXQp7YJ6jYM/D1yJ18T\n1ZyEbZTPlh/w5Cps2vPZOvSmqgYn6yCP7V+Hth4TrbkAF8qv+C1SOz9AXZt5\nGs6c+JuU4hKaPh0gVCXfYCHrun4esesUDHKgJNoUoKW2x3vLXl/L8HFSLqDC\nnG5WC1GV3kHvVXOBEnV5cP5NYGPDrTSBQMD4lxYj5+zG20m3DC17af5jEZcu\nm9fGZAWq793LlDEuBF++1ET6vxlOvC+wOHR3QknvInhDZJWUoURfHIO21Ng7\nS/Ja\r\n=lM59\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC5Dv0lJ4zy+Et/hd3LubdjLxZZOWZjA/7zXqpZ6GNZIQIgKDrQ1ATa18oCjuGecjh+XsWWAssQMbrIG7mnzSH/o1I="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev.0_1573532945262_0.9119022307182936"},"_hasShrinkwrap":false},"1.0.0-dev11.0":{"name":"plumier","version":"1.0.0-dev11.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.0+9071b7f","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.0+9071b7f","@plumier/mongoose":"1.0.0-dev11.0+9071b7f","@plumier/multipart":"1.0.0-dev11.0+9071b7f","@plumier/serve-static":"1.0.0-dev11.0+9071b7f","@plumier/social-login":"1.0.0-dev11.0+9071b7f","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9071b7f7b34a2d50418896085c9c3c0b110bbc7e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.0","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-hq81QYl6E/mxETQpAlC7LQtv8IdrMJBE0YTvgghSuLnX8bNheooH7g520S61zi1vDx6IjM5MN4HhnrmFfOU69A==","shasum":"3b1d9c2a218cc92a7c7969a1c7cde49c17131486","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.0.tgz","fileCount":9,"unpackedSize":20141,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdzzZDCRA9TVsSAnZWagAAwfoQAI6UaEwgvSNgn7+s43IJ\nlNpa9cjQqWTXEk3AoyMboZvrmjU2hle2A8AsaTflAWsQAcDy8hlPUuxuCK+0\n+bnZaN/ZaJOHdwS00duRT4Nj2fwL1tL3xxHzH55ViSMeivmaoeyBqAW8GlJ1\nBQ+TFuCBXg2XDjNcziTQaLzPIRDRL9WeR7cQXfqAlVj3gznoFGNj+UJz1SdT\n8KZPYZG+hA+hUF8lyepFzU+B4YQpHW264v+6iEX/gRP1wwzHJ7kG7+pXiFJV\n7z6VeL0vY/F/YuEcbYjjYiko7RPknD8/zzxYL3SKhC/UPuh4XLPgCZ4NUQxv\nAN50FVRc1OqFUY/XCDhK/XgKJ5yJ6+ZF55TYEliRhz9J2yfDZZWQTnBHMBej\n8OaWEB6LKPjUOJ45X/59NxuHBIrOoW9kgMmTApWYbPDSMU1/fZut3s9UofmN\ny+cbDOkMk8Bki//Np2PON0JHsBh02X8fVN0MbxzxyXVedd4l/YWa2GN+tJTq\nNoGZ2RH5izLA4rxIrZIb+UTI3bch/Rfpd3lQ7UWOYnZj/Zxqx17iFFmL4iCM\nh3efjAf4guCvt3FN2YTERhvatpvL+Ct3MdY3KSAbuDAT7Ko99pB2k4o2csWE\nfWSM5YLM6g6dHgEdtchXJncfXMxYTZro1USqDr7yAc5eGfY9pCCH1oCJzLBa\nnenv\r\n=Lwcd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDqswBbwVv4b2nxdXyxI12P33pvkN3wyjyaKoYSoUUkmwIhAMumUdqaYq+5gCSgE9baEl9u4vEiJDXXg0+lNdlKYBwQ"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.0_1573860931491_0.9765958907056129"},"_hasShrinkwrap":false},"1.0.0-dev11.1":{"name":"plumier","version":"1.0.0-dev11.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.1+643563a","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.1+643563a","@plumier/mongoose":"1.0.0-dev11.1+643563a","@plumier/multipart":"1.0.0-dev11.1+643563a","@plumier/serve-static":"1.0.0-dev11.1+643563a","@plumier/social-login":"1.0.0-dev11.1+643563a","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"643563a991ba4b4b138a261c25f2c017daeded18","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.1","_nodeVersion":"12.13.0","_npmVersion":"lerna/3.18.3/node@v12.13.0+x64 (linux)","dist":{"integrity":"sha512-gMpxPkmLnY2T7wS27PRbHY4UTF8HrzoHlZ8Ry8WlAB2Cm3fg+WhLnRTAMINNGcKJS4nBWzZXIUKEpvlCO59bJA==","shasum":"74c1251cee412a52afe673ec7d8444290370c98e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.1.tgz","fileCount":9,"unpackedSize":20141,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd0Qg1CRA9TVsSAnZWagAA4ecP/iMUO6mo961PtxCzKXIS\n2arO5wTBhoe75nkd3sqxFgsUhNcD1abVKPZ515nbY4kylDQK6sCGxJTx7pcS\nNif1+8qEVKhJJYyuG0x23GK8CSzZX/6Kn+nc3vwNQZ2Qd/N8JSSt/pWx5oCu\nxAx7vu70OleuHu7ifnrTy/2RpoAy5pdvs40KFdaFELDw4PsUo1hBv2QKa/9r\nig1fJGwYvFB8pYPI9Bj7+aohzGVx2vGtZNHD7175tIF7EjblG4bz4xcxO49H\nMa07x4ZAukRgop+5MJrIt12GlCqfZSVMNpxkuQ+LF9uSAjf01HCsOX//34/t\nk7BnVivLJCkfKk5m1l6LegPBu7Uk6VCjWsfb9KTM8sf7gSoWagDs1EJtC9sr\nkygSlk8EyI5zR/hSqWGJFqWHe8ZT79rzqn5EcaK+2TGXhGE2yxwPCGLFlb1g\nQJ5e2vwV4/zd9W2H06lEh4zzix+V5F3KW/U51hDg0cxMPW+fynE9XGq9WRPC\np1r+2LOyFucUUrBIjgZXiDjZdlva5X8mjSNjpJVKPpGNPZ5vZ3NJhgGif8PG\njdhcUWqVx0CVVSVWVtd3TbaU+uiO3+Rdczff2hwKQKvodOWjl31ZEyCMtZWH\nXC+zNvswwpaqgwIVPckoJRyBAIcMbztkW7ZcJNuktIoqmITpSjMjR+y3etqe\nOv4i\r\n=Ty8H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA8AB7hI+F3+jhUPoCDY7Cyb8brNtC5gJorws4nv4hD6AiEArliOzIEJV1ZUfRGnxPRpb4OJ+xXLwCuD4PUuOHqMzJE="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.1_1573980213617_0.9277810835215803"},"_hasShrinkwrap":false},"1.0.0-dev11.2":{"name":"plumier","version":"1.0.0-dev11.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.2+3fc85a3","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.2+3fc85a3","@plumier/mongoose":"1.0.0-dev11.2+3fc85a3","@plumier/multipart":"1.0.0-dev11.2+3fc85a3","@plumier/serve-static":"1.0.0-dev11.2+3fc85a3","@plumier/social-login":"1.0.0-dev11.2+3fc85a3","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3fc85a3209fc180c81b18c2d04f64b94882f80db","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.2","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.18.3/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-QMa7f+bL5iFWvMHX/XnIQFcY0pzbUU541xvf1kp7LRQSCYzttXOp0iJSDUnu1GKIFHvWH1DY2ZY9Q+RkhyLFsg==","shasum":"8035b463612be705af8919aab1031c60e3090ff1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.2.tgz","fileCount":9,"unpackedSize":20141,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd1HBqCRA9TVsSAnZWagAA9XwQAIRJosoOwBdP3tZrkX+6\nL53jkj8QV+XnrrlqtcQ+CfaotKb0RneRJbreCatNViRhHNKTMdzmuG/qnETH\nJ5s3TyJSCI9Z4mQvMgqTFVjtefTA/W6nVdJuahmjRk/Ql8Y50/x4UOQXjxgZ\nS36Cw7t2Nri1HZvfaIixBBoyyhoaHMjBYcIldVOHT0NAy7BhrpeaS/BBUwTv\nXPM08vmlbu/lqvhnKxwiWDG0AATnjRMUVGIy905FTQ5AYUUWTGQ4KoJ55EE1\nzJsEmHTYzb6shC1PTGPUVl+maE7sGaz3ACMXnVbo3ArrPqtxibyKKDKeWYuU\nMSjUkffaDOGTG9jna9xusYsjZG37FRihrY0mWh1KPYRvC9nwRumoiRJc3iKP\npLbUgaYdldnZgCW0XBB9/MSdRAQwCSX3QPreVug581z27Hl0KXoh/NQjk9ZL\n/0cHOWQTeQJi6OZ0VrxPPvrT2sQTXD1GNQnve5rGwUEpVwrgXTKeo9F/5zDV\nE8bjlM9y6stwjFVL/+roxNkHgF7zo4ysqT1XMQKkraxexbjrKVR02XFDqKEb\nVoAEGTYKRtTN34oM0/Ry6ZJW8TbtAAakC3/fNQjAxkco9yhKznXaiVU77znY\nVixZiMDp4+kvVKv6ER6dOeqvM+hiDULlrMEyaSwTEUFD0DUCe4F0s3DoRA95\nYP7B\r\n=cJMA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHOzwzpwCdVGKLpuR9DJelaZVt8ARcXeln8+7Y7ZIkGAAiAhj6S7Ef2pZuclNtXMKONg82j+luo3Vlj32WRpH+N9Tg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.2_1574203498305_0.9186332490058478"},"_hasShrinkwrap":false},"1.0.0-dev11.3":{"name":"plumier","version":"1.0.0-dev11.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.3+4023ad0","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.3+4023ad0","@plumier/mongoose":"1.0.0-dev11.3+4023ad0","@plumier/multipart":"1.0.0-dev11.3+4023ad0","@plumier/serve-static":"1.0.0-dev11.3+4023ad0","@plumier/social-login":"1.0.0-dev11.3+4023ad0","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4023ad0ad94e738ed1fd7149e070ef9d71694d26","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.3","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.18.3/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-tFJncxR3JLClsujhs9Q8xQmD/SKC0AnV/9iVgAia5ZpENaGz3zCB9TO6xwio16/5aBf5Dou+q1JSw5iQGqtw5A==","shasum":"ef408fc867f38ef4a596865a24fa1f9bc1c15345","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.3.tgz","fileCount":9,"unpackedSize":20141,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd1K15CRA9TVsSAnZWagAAzagP/0S8O2onDbrFxunLB1tl\n2j78fRt7N9I50X8GX3+9cEgPhsermNjPbm9IrJuVp58FfL86tBADKpYlnPYD\n3cwfsQTIOkc7Yo93rXSQzaoVCGlXaUP3cOEPa+SZeHA7qwxZo9nPMYuSEGdA\neKXIRXcw/aQ63GM4HUxf6l+muVxP845jLjl+EXPU0uNNetmUJX4ODByR5zw4\nUh/jIV8c1AdhQvvgQFNMJp33JfQnqgGjlSETl0W/f7oyhZvtihjm0ReGTL0c\nh1mLo2c57bRDFRtJxR6bHmOqRh8TU3C962wKHLWD6B8090h0RiwkkpMjeCWV\nOoLQTspJk+Sr9atS9qhwfxW72MKkgqOfZAXauCQ3mmhIEg1a2YfziaSCWU0D\nrSRQT5djf6QYrzOYSwCs4hQkK33BXeLEgPafDCTKOvCtUhhgjxY9N0kGQW5a\n6XWUyhOtGcjjfjnL9/yewXmUE+pEEFi93dw/LRiy5Zq+KB2UwOF+em8yPCHQ\nlungGX797G0YBkjcu+NmTAfpR7lmluNb6OfSD5emkXXZG3GGGrfkAu3F9q/2\n4DoeCxozR0+VEBo5JudiVmxS52bw0hAPu9+PnDVcXVM1E/JotjdOR7zVqJKC\nSxbzA8XMudM6R/G9mrBDj3gREb+KpjHH1H5bq1Y3xMaX9fRznKt6rMntgoQB\nWLml\r\n=8YSE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCOO8l0GlNvIw5//8tPBuopdps9wbLepehtS/+TaLs74gIgEo3fvnyJ33Ud1y++z5W+CZIOwuYeAkd6rPHY9Rmi4ug="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.3_1574219129183_0.208312398604809"},"_hasShrinkwrap":false},"1.0.0-dev11.4":{"name":"plumier","version":"1.0.0-dev11.4","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.4+abaf721","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.4+abaf721","@plumier/mongoose":"1.0.0-dev11.4+abaf721","@plumier/multipart":"1.0.0-dev11.4+abaf721","@plumier/serve-static":"1.0.0-dev11.4+abaf721","@plumier/social-login":"1.0.0-dev11.4+abaf721","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"abaf7219e8bddd92eae914e23cd069d122e9d048","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.4","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.18.3/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-jd2N81Si52DgpQU4qb3RTYYioyo9gn/e9L9D22P/Uh5Y7H23A15Gnzbu678hJ1e8a6Rd+Fsc+aWni07WK+RjJw==","shasum":"a7c568d57f844aca27caef618224dd8923624e34","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.4.tgz","fileCount":9,"unpackedSize":20165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd2gOlCRA9TVsSAnZWagAAPw8P+wdXGK0hGdSmDqPD7/Hq\nnijq6Spd6Xz8SAHH2T1y9sB3RrXwGyh2Yjppye5n+vQmXfZd+ZlwmXuxR4iM\nuqxql11RQtk4RZ2JMFRFyeAiCe1z9GP/RjBvNMJSoo66g6tDV3zHsiNoZDLc\neXrGa4QqSTW7LfB9pF46hfoYyhCZDikygsIP90z+VtVhuslhx9mtCtCYSmy0\nuzcUKR46Y1GikOec/arAOckzRN0Cumwbkx5ok+h9N1Rf/h1zRaiCquzjWOEf\nHVI724ckBRheFcqIVM/QnWlExXzy8tyhEBUgB757nkavEGYkqr0ep/5Mpsu+\nHBA7HQ51aBsKZvRGAWQSjzrroWN3yo3E6wgl4/anR3cUE31UBYFdr5KXF2mq\n+JRQKSXGVXeE/wYfdf/RzgU/t3roSOkOcIatkrVLnGLS9Ly2DSsj3wRgnEfk\nz3tM6DKv1HtqN7GB7xrJya/NH+la6OjVZiv8s8f6VzyM1BFA+otorrbvWQTG\nVtO+d88edLSPOEhF2EFb+4NI/mvfZFDvUZzWOPYI3QdhMUat2eDxDxP7iapF\n9mWRpfdhiuxFQ0yNRdAVw60EkRLDTjS39QaQ9kDnf7TaxMQXzJ298mMEWNNd\nDlpSjytFC4/3yiLTDN3tirkQlZjphGR1EZhQmq0DmLv6s1xodSVXu8H671wT\nVcv4\r\n=uzdi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE5f20L1cqMENU4fGC2c3gEvy5kmWqLQ08QABszCAoa1AiBTOenYIEzxahXadCNZ+GGKaMj6ce8xwo78ECqhOpYckg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.4_1574568869369_0.8126627970908524"},"_hasShrinkwrap":false},"1.0.0-dev11.5":{"name":"plumier","version":"1.0.0-dev11.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.5+adc0c6d","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.5+adc0c6d","@plumier/mongoose":"1.0.0-dev11.5+adc0c6d","@plumier/multipart":"1.0.0-dev11.5+adc0c6d","@plumier/serve-static":"1.0.0-dev11.5+adc0c6d","@plumier/social-login":"1.0.0-dev11.5+adc0c6d","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"adc0c6d8530625c9d96b19849495a2186e6f1363","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.5","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.18.3/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-3bhThjTxelE7CkGORUP8Q/Q7oBtGck1AAU+Iy8MbkBg4aWfBKe3wMV17+2P60oZ0EQM1X1BxORjLUuidynLBUw==","shasum":"dec63c9475ffbe7de2683eef7bc30f0d0aa1ab8c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.5.tgz","fileCount":9,"unpackedSize":20165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd2xjFCRA9TVsSAnZWagAAxokP/i+rzLDN8CCxqQceMvos\ndyTklHfz1u8oDosyHLiWd48SQpTaj28xGnw7Z/KkVnMzCLS7mn8AOMw3NtdU\ns0phUy8ADVJO9NhPZo2lZSmwEOsn1D2MnVFxrK7sX7mm+z0+Xj6nxWYsmHsZ\n/enp+cvIuj14zzkgZat/c5zEZ4JUxSB//zFsoMqmeW/PxkMMq6992A6PjJxu\nEsEC4lI7Bih/zqOVdMJnVM0D+zDCvX87ors3vMXY4Y0MNnq9cu1V/aB/Fc09\ngaQijyfodWinCL474o9GyyMpz2SXgVV98qeXiHl27tY6wyR7DnVTtmEnmM+B\nr3T985Kgh7FmQNEniSxfLN+CsbW0r1GvXfNfNgyhVoVvZdk+qbPMRCr1HMOC\nACJeWkSxsyGqWf9pYtioWpcrQXIk/QCpagIk8XrHHs50m4d8av2j+7AOnXFF\nk/9XydnkfgBoQnL5OKlduZGsF+T/BEOwx+eL29KHhpN2umnqq+t/1bWmFsVV\nBYOR8oQdch7wMKuoqTWqt+kLlIFaoHJrFeTppRSNCEqkOf0juCe5N3Aw5aEi\nqkyI0HEQkl3gSCLokIYl1zP1G7Y6SqkATXRdLIM1sbRl9fI3/+qC3It50Gdf\n3vNKXcmPz/6q+NMApbkE34EpKj3zYlB8mnGYxrZ2ohOJJ8xp3RZ3UYpLKHGD\n57iS\r\n=faSu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDnCV+M2+q46JuQhCwbv38uo08POOgVo4LsEi7shybNxgIhALAx1oz1u40lTX0/+euxSmHItCHXwsLASdTOLhqEGQcN"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.5_1574639813454_0.047530572836111507"},"_hasShrinkwrap":false},"1.0.0-dev11.6":{"name":"plumier","version":"1.0.0-dev11.6","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.6+17b2ca8","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.6+17b2ca8","@plumier/mongoose":"1.0.0-dev11.6+17b2ca8","@plumier/multipart":"1.0.0-dev11.6+17b2ca8","@plumier/serve-static":"1.0.0-dev11.6+17b2ca8","@plumier/social-login":"1.0.0-dev11.6+17b2ca8","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"17b2ca8dc97667fbe9cc8e386d4ac6a000054bb3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.6","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.18.3/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-461LIn9OJRPj5FM1jxj+Twr9d57JHBcBSMcjmZKFl5oxC9da5sx9d2/ZW9xu0SbhNp7S0cGDO+ZwRsx5VfJi9Q==","shasum":"700034b5640c316bad9c596eb6daf8e8c9670a4a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.6.tgz","fileCount":9,"unpackedSize":20165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd3HNfCRA9TVsSAnZWagAAbSUP/3ududTNyWyT8n9UXvR0\nM7RbVwGozaUx86wAEnej/s4isLS4fuOZTOwz7n95eiBAJmP0z5q3VhgtzUhE\nPBF5CNuGLroni2IczPz66/84HV0C3PgML79oMNljLlvZWSqGayxPjauj2WIK\nNvikKg1vPZMBB19eOYMEy/mMBDVjxZJAZAc280hSwhrxDI5PYqE2I80eQ3Dl\nYZJ28uIbo8P2S9b31kD3+PR8TyIUFNOPT2JRxrsJF9IAi4io41sQy3LaUJek\nIdppQwVIHwe/wq0ZT721ihHoTQUTJZnMkMZVfe0RNtSykG0qF/Xnq5Dpvgwu\nEHFw36OitmeD0NO4OluSBfunir+EZOvqpTfeCGzA3CqsHfTzRjffd1usHDir\nXyLGdEHPdKtodL/CprYIK//ZrxLcF3UuaWgFTjGWf+IJ3XoQoe7ymxu4zwIq\ntxhyCIgxkNb6o0RSAf+HYPoojPqLk5ZNB5sgeNkHOtEWz7Zk8taHJfpsMQgG\nBtLemh9ZiOd+mGjCcylR/J1ay92CmHOoAU3IB0Erf3aiAFTOvrHEZLgHxo7C\nh7i6lEOgwAj5k8x7rd5DqLTp53A/SFN/RzquDvXWRBy9JH3ftOvD2lcScvUI\nKx5VDty6zTpOH1F4yFRcaFYX6dwcavcZb83/TcAJPQa59vOepzhYIW9DTOkg\nO8x4\r\n=N4j7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAUn4ekAC106vRfKCpW676eiM7d/UGdg7za3VFnfEv0DAiEA8yepO2xMcAWJBKGUk1uOO0JlY+5SMVOLuDf2bQZkp3M="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.6_1574728543566_0.9174941556232703"},"_hasShrinkwrap":false},"1.0.0-dev11.7":{"name":"plumier","version":"1.0.0-dev11.7","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.7+3240ad4","@types/faker":"^4.1.7","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.7+3240ad4","@plumier/mongoose":"1.0.0-dev11.7+3240ad4","@plumier/multipart":"1.0.0-dev11.7+3240ad4","@plumier/serve-static":"1.0.0-dev11.7+3240ad4","@plumier/social-login":"1.0.0-dev11.7+3240ad4","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.0","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.3.0","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.1.13","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.4.1","typescript":"^3.6.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3240ad4590e836c86e2f159d52884b338fb693d2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.7","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.18.3/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-YTXsXQO5aYbvLIEYPYVvcRf8umljxaikuSTpXlQVCx6Tv2wvGan0bY2hREg4w8eEEfKszHZg9cejvXXyboCifw==","shasum":"4a7561c06baac1fb32d421dc20c2f35d524551c3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.7.tgz","fileCount":9,"unpackedSize":20165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd34VPCRA9TVsSAnZWagAA4o4P/izakeZFNYuElGHUrFGC\nErQXkLr/umKrnRAvdHP4fJaVT3ftlw4MX6Tx7dXXNMSKRNMB5VpHn40R6anJ\nn5/okfuiE8g4o2Bn1QhNadE2Yr1Ih6pPW/u+oqFQdCOuswCTykHoy/XZ2fc4\n04hIkw+v1O0wJ2FIpALcYyBmqw3q8XZAcUz8BPnjlK1ko/S1Q97/sv5tZSKo\n6Vpqd5iK1iTpsyz06OqMjDD2Ew5sTy7Bf2GeRkCe5rZ1SRXXBcoWyNjF4d7P\nteYorKA1RnQgjnL+lESYbELXfBwrQgKi/9MyTSvjrU9vWJ+3yzAvSU9YDQH0\nzZU2Q5NHAdQD2vYfufm4xYSMi9xoCoa2JDsY0igBZnTKtGTTPdC1IiDIDoPm\nKUSU/jjoeK9I2WYTkLsvhDEpR9uQwUAkrj5D7dAbyWCQp/+l5kxDRvKHTp57\njxbO2hX0PVnm3S+K0D/LMy6i8xLPejpBfhudVS7SBLdr+xajpcrhbBoYQ92d\nSWvYLkiPjlmVuVsZ3V5k7XPrl5G64uCQuUWue1ZJc6ZKxzpHvB76rJvBRVqk\nT/rNPwC1Cru5QFh327VtJYLven9pRDOd7pffrb4x814FedWdIO/fZF31RUgZ\nNakp4R6TnIA3pptztcVwwoPYrLh71R/Z0+AEtP0M5kMpDZ7xnsyY5JA5CgOs\nlOJx\r\n=OslH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJLFXfcIMwc/Z8UIHxFh57wVANdLUSqcgq4r9RmlTBWwIgL+E8Nzk9D0fePPZ9qKXpmT7chByLJnI087GYG8wsqBI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.7_1574929742622_0.27336453450001974"},"_hasShrinkwrap":false},"1.0.0-dev11.8":{"name":"plumier","version":"1.0.0-dev11.8","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.8+434b5ca","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.8+434b5ca","@plumier/mongoose":"1.0.0-dev11.8+434b5ca","@plumier/multipart":"1.0.0-dev11.8+434b5ca","@plumier/serve-static":"1.0.0-dev11.8+434b5ca","@plumier/social-login":"1.0.0-dev11.8+434b5ca","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"434b5caff76a838ea1acf3641ee5ee0a085175b7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.8","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-jU00W76X1DgOIN1X0fJy9w0ObGbMSOijZTqkSfOjeALnYbPZVgOBkasUd0f+mFq16UsUcyU9pldAbL1905xJSg==","shasum":"2e989b0e25039a34fadb3a4dd30418c8b9e74c1c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.8.tgz","fileCount":9,"unpackedSize":20164,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5GxQCRA9TVsSAnZWagAAPU0P/0iCIcyyCJRlj3BAgLaN\n8/UbX81hZfTLoLs6pW9NSb1OGf3QM9jMsSxZa8GkOA2aPDi6Aaa4++M1+8gs\nEVpT7lbRzDc+ggLH7tZ/pgseP223Bu7ueAg3o76nnzPq5PUwup21rwMU6dPi\nrxBSWdcDkg3jhegSCaLMkZH6JQEfLNy9JZ2oXygNxAXXehhQTXEiyl8Z/rAW\nV6Ar3aL9M7EytDhKlpWfrmNXtEqoV1fCdT89zZniP0YKRNLLrgHzWiArNxUq\nEV1KyhV8RGqLVLycGrUn4TKDyIIiZMSqiPxxZ/OyfhwHZuDdjEIwyHzrEZER\nj7UsHAqwb7FND7dHpq35hILCDsUqJSTN80IJo0kFI0U3Y9KKGUuMHCuXLpPy\nMWxNWK4u/hElPqNTeXYoqZmT1Xvvl3UORr74ig3l1ih/kCEwq/A/J3zwwgjs\ne5aQ2zAsBb6L9bxCfDe9jfYGSXQSuGZX+dhcf+l4Rr88cPHQ3dQennmWuGzB\nACI1DcxnTXN6cVdvFtxOG/LwpX3YKO5hjhb05eb4rYLqjZhuUomMvqo9Qd9l\n6Cbm4oZRRMg82B1axuVZtc9Psrswqq7lpg+XLgQP/fL1nO50IHjuRZm5q55V\nqODR5ivDmwPmLwreH++Z5El2seg9cgjJMlxl+p/1cDBu/0KJurFu5gla6mJu\njCAV\r\n=QO9d\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBfk34uVNY3WABFjgR08ofdG4IeoavX+Z0cir4IPz+mjAiEA1KPrpi/AxmnxP1VvIvP9ijc4FhZT7HVDMomyypYZJPI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.8_1575251024050_0.47584813433422357"},"_hasShrinkwrap":false},"1.0.0-dev11.9":{"name":"plumier","version":"1.0.0-dev11.9","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.9+cceed7f","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.9+cceed7f","@plumier/mongoose":"1.0.0-dev11.9+cceed7f","@plumier/multipart":"1.0.0-dev11.9+cceed7f","@plumier/serve-static":"1.0.0-dev11.9+cceed7f","@plumier/social-login":"1.0.0-dev11.9+cceed7f","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cceed7fbd64dbf2da5b8a14cbee3dd483bdaa0cf","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.9","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-l8MGpJ54seJZK/8+d733cFTbuQtFCEoLXHO5HAVxl8mUXbTGkmT5Akvr9kUYLlvgIk45mjJCtwOCGFVX0gXPEw==","shasum":"086fa0fdf038c71ba8499965c97533985b14a453","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.9.tgz","fileCount":9,"unpackedSize":20164,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5J/uCRA9TVsSAnZWagAAJfEP/RU6qsW2jdIwfMueuzRh\nDTEgBuHZu217J94AJTPZz+YmSIguRtH5CZhqwQg59rbosmzmx2vKNw+wJztD\n16i5a+HBYeiHBSaVy6GM3TVzuqIEqVWe6qkWTPjagMePfMuiznUxhbuVEauD\nzDhIpZIxzvser/hwudX0cQryhMDhybBQ37L/3l+jsgniJWVaPV1Xg/GFW7NO\nLqs2px+FFuGrh2ysVmYkbyi93fT7Der5KjC8+lQtHbvi2B2wR+KCtqY3BexT\nruHkhyvimK3X4FSKTdSX9ZQNhfv9UCFPaJwF9oJQEjSsKykzMv0rn6Jzg4Yu\nwHQn0m75nLXbXrKujxxE13ImONgeMVqO9bEDFKkcAnz5FTZisyWmd+adFJuh\nfrOFA7aoLx4NlWQe7zZRxW1iqcGewP4HoUtSy93Gg8q0gPwJmDDl+HkpQn35\n8Wju3xywANc4a8FCb2vSZEsyOib+qZauFy9E6nEIA3XjbA/EqjJCjpNZuBcZ\nkZn+Ek3Mo6f0MSrjkiAwznrgRhL2/Dnb4KKJC83kthEY35h33mrYZNzEihCc\n7zca6hZc+rE6nPsIFDCmW8xXgHvWsdJaVeE/0b16Vjw4zTDmhIc0IxOWZaWP\niYmKGlUvW5q/I2L5Uhl28WaH6IWGSfkEE2B71K8QULKbOgtfyt+s1Rzi5D8p\nz+67\r\n=JZNh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCDigkpEqDTpyMTrjSn196en7VVGISTFp2wAziHYCZfiQIgBC4uMxKv18bcmsVukm9t7/r0WP0lx4kKWHcnzXcZCRk="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.9_1575264237946_0.27513665061624515"},"_hasShrinkwrap":false},"1.0.0-dev11.10":{"name":"plumier","version":"1.0.0-dev11.10","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.10+5be8aff","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.10+5be8aff","@plumier/mongoose":"1.0.0-dev11.10+5be8aff","@plumier/multipart":"1.0.0-dev11.10+5be8aff","@plumier/serve-static":"1.0.0-dev11.10+5be8aff","@plumier/social-login":"1.0.0-dev11.10+5be8aff","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5be8aff42b3ac54b659eb54dc26e656c78c01849","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.10","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-sfxEZyYN7aoOhkURSvsfQu2A1r9mxJ7fMxQ6GPyu2UqBj948brXMQstnqWHIaVDxbl9lvrHV6eIDWwM+MMlZzw==","shasum":"eda40de0e6dcd46e47b496ce6f5c0232c35a4748","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.10.tgz","fileCount":9,"unpackedSize":20171,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5ibICRA9TVsSAnZWagAAsTYQAIjU7K3fd72XSHWAPvma\nQk9DkUkpvGTzQvxX4SeLqbxoeht8mIPrRlhDG9sREarmGQOXrJ2T0cME+Z3g\nA40iF187vNbbJfpoxA4dPOCbbYKVPORcUJ84z3iqOFzB2SRCCgbRDAQRaC9p\nwgbkV4otZbIwbprME0VdTylLFN+BsQl886WFFXyzHpPE3fwO5t5ImT+sBj+/\nkLXjPVJ0aUgS343H0N8/6EzxJZD31h9GdoSQVEFOGhdQ1keyiznnn3vfHBdI\ngn+KT8IzG0GVQN7PQL4KcUbAcPKk5pZGl7McbKfiN0vvVFDyqgBR3rvN8tyy\nwOJVuObvHKV27XBS1gU1ASGnX9Sn6Rtj9UmC2iS3kmR/izXL0EriguIRsVF5\n5b+FmyJm1MToAfx1Zm5s1fC1YuA6pKUcVRgXzSOn+3boK4dd/mtlsKgJc4Ah\naX+OrkW0U0v2PAHuLDdEqc63yi2jUKcWRhCytKXBQ09OzMI5oE5cDgBPMS4R\nYiXCEBHG9jkf2Ss4pCCyFlvihVKrGhnv4qaQYscjekxfKAG5afh6mZzVEFG8\nrUo1UkiPaZ2Sx1SdquQn2jl6BP1rKAG0UHyVpf5wBFnVs1YnI0yj/jSkAEwn\nmgpwVci9aqld2JW+3VzoFLdpq5oRZsv2v0UxSKsZJZ294LyIlh2rQkRcDDTI\nlCxs\r\n=R+/e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDDlm+oxSGE8/wsWIu5iRzGFY+5Z3SIi7HeyOiOee0jrAIgHGQf0ptAslEl6bDfaKnDlzRVQKZWiVK4QeJwSVvWyXY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.10_1575364294461_0.1755557801835217"},"_hasShrinkwrap":false},"1.0.0-dev11.11":{"name":"plumier","version":"1.0.0-dev11.11","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.11+27b68fd","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.11+27b68fd","@plumier/mongoose":"1.0.0-dev11.11+27b68fd","@plumier/multipart":"1.0.0-dev11.11+27b68fd","@plumier/serve-static":"1.0.0-dev11.11+27b68fd","@plumier/social-login":"1.0.0-dev11.11+27b68fd","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"27b68fd6f5f78b9dc60abbd84a56081cc02801bf","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.11","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-7CbqIcHFcVkxQDIfYQ0y1TfeWs5K8mJ85MKfgV/GidDtDDslzx1ImQb377i1hs1cEf7ZzTvVqyUOeh3bSCeILQ==","shasum":"334b8fb104e429d451568cbad0fe43c7eb3319a8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.11.tgz","fileCount":9,"unpackedSize":20171,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5zo5CRA9TVsSAnZWagAAFtsP/A3QxEPgwaNtDbQTBL3H\n9DoH0bFcZeJVYR62WVXWKjlf3aZWCqtAt3IBJX2yCw2Kn8WWNwtSWVr1ZRhD\n+id7SNEQWpHk+S/jZcOkLi2supMe/cKYcNVd0S/AoMQ7xzNPc97a6KZirXKq\njI4KtThgYfq9/LO6cxYuvD6fVF/pZyiSBoFC1IfL8c+9c3VtVMLPX71wTpuh\nN44KjClAfY2j13Yzkh7mCIYvz3D4yqd8UpV4buS2vH6KlmpY8SIQ442qc2sB\nJFzq8fD6LrUS1e4jYjrSyfxawd0pvz71qQgNFzxo47W8mxN/GqrPE54z/4Fx\n4Ho8WycxaZLn0lVYzvQMjOyQh54cqZU6DdTRaD1Zk1W4rbVgFfYQXUn3bcDq\nfhgdTXns0ttKirEnaf0WhJC6ccU1E9Wxww3MWhEu0ERS6wb/OQgp1jahd01B\nzL8+OZuaKY3ud9EeKIWd+XSmtsGbfXr7zubjiDRk9Veo28LE2GlCXxM2F12q\nR4jnS4644rUpZgBGkSpvaXHgOmC8bOmS1tVt2NIUqVagZNcu1eMFPzCEo0cp\n2u6v5G+wle7E0/U0pQP6LV5hsunFrznlUN3fj+PbrwhUMADOkrwxR46sC/k+\necZfQJJYZ7FbjXLQKakQFXS24iV3Ta1cUpOjg5NKVvo958IEp4E/QFv46IVL\ntWZ7\r\n=ESUS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFAB0VmIceic93IzXZlrFCt9IHstLanccL9JB74xT9fnAiEAgHNfCaTlQbGrBC9/qjCrGx/BBCG6A/EkO9Gzu1lNx0Q="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.11_1575434809198_0.2995200856586355"},"_hasShrinkwrap":false},"1.0.0-dev11.12":{"name":"plumier","version":"1.0.0-dev11.12","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.12+54e9548","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.12+54e9548","@plumier/mongoose":"1.0.0-dev11.12+54e9548","@plumier/multipart":"1.0.0-dev11.12+54e9548","@plumier/serve-static":"1.0.0-dev11.12+54e9548","@plumier/social-login":"1.0.0-dev11.12+54e9548","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"54e9548fcbf9333744dc0eff88e832c887a1ed13","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.12","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-CSSr3jXFMjOfmPjyqMCk0+DRbqbqOBdrcsABhc457C5r7xXM6L6Ez36jqv2LjvkeKl+BRffI/+YgUeUmYlGNRg==","shasum":"9e6d2ff4cbdd2d7ac0ee1000fbd087caaf8d631b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.12.tgz","fileCount":9,"unpackedSize":20171,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6MLeCRA9TVsSAnZWagAAGHEP/i5ZdsDcOOG3jkRruIA1\nrlbyq8OJ+BF6jARlm256HDi6iKJB5+Vu1bqFLy0GNybEhqR0Ct2zJK0vVSm+\nuaUecak5Tx9DuX2OZgCEvUGiHqj2h3jJHiq8bUxc84lgVE+tP1aBUq9kFY5s\nFeTVEP/IFhMranTzXlzLIHIZqXUeIpvM0CLq+M7IMOwnI3apqRSdhuuUBd6k\nJfStflLocA1O6nnxmbGKdXpg2DPa/WH80p30KuiNXbcsvIzekZJ0ZrpFLgFM\n8fQSXVmKe4KUz9jl177xQfVkvTNHhX8HX/LinRa8J/cnRVac7489CqtspUmY\n3HWi6UT0sSZtritSaHMAKoiPMNkhbZitjyN2fkGQDwxR8Q9N1e+SxcctsoW8\nIwXnpiTMrR6TmcDbKo3Hy2RmGSkNZTE4HCiFQpTU85esgucQzMP/YVzJjxwQ\njiFUvFrPm7ZXgNSEXpRXtTLBYlJAj7w2ROtstjnr035MFX/AgLAC30+yn/Qm\nPqXZzYggKX5YiFEG8lRYM3e4yxHlzTA7KcCTUSlJwDzIrqf87HaAgXPe57Dq\nOIo7r05/u4wdJ/U/nN1Jyc3z0r1GnuU4/5/nf3zScu2jWJ1YdDFRPNWWhxjl\n3gMe99XzBuZMozRTRwu4G0WSOMABvQzk93V4Ejm7Zi1dbenIjigltMjAE7zh\nCbQB\r\n=ammR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJBT8csC6x7mzaM2yx6KBO2uiwDGyUQrYFxH+LZlzXGgIhAMW7ShiPEAtwa4KHcWdmsFo8Z9nOIAVKKMzi8T8C/NTh"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.12_1575535325896_0.07570906974845704"},"_hasShrinkwrap":false},"1.0.0-dev11.13":{"name":"plumier","version":"1.0.0-dev11.13","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.13+8894271","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.13+8894271","@plumier/mongoose":"1.0.0-dev11.13+8894271","@plumier/multipart":"1.0.0-dev11.13+8894271","@plumier/serve-static":"1.0.0-dev11.13+8894271","@plumier/social-login":"1.0.0-dev11.13+8894271","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8894271ed906b59eece466a265e93a55b12155aa","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, @val.optional() limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.13","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-9ubFZUPgolZXJFkhA5U8WJSz4yPyXaoRQBdSsp1RlCbLRMrrFX1Vs3XCxPF0SIXZnzgWroiBXv1c7Q0IKtttoA==","shasum":"983d84eec87b03e404b89e4a9f589664c3bb9c26","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.13.tgz","fileCount":9,"unpackedSize":20171,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6UHiCRA9TVsSAnZWagAAmkAP/2O4B5hs6lP8ZDH1iRfA\neROzKXxn0RaRWiMeI+NZ0JcGe3iPJMa5kRpsJ7Dg1DoP5YnQiuMW+AzWGOCU\nCdVhD35r5TmrevPWe0gv4V45qZBtee7n6kKjwCOtxPgG4Ik99LbSYldauvtG\nsIe6kx/Rf8/f8bgAadmuKposi7HNZuR1srKoexQpshAyAkN6tEcGBGbSEqe+\ng3F93CztvauLA/sKroOpN2Ug0XBZ18a8Fsvr7hdtqYERDxmYku9nZydOxh2o\n0tMDaZPcDOdJ63HtZg5CyA9/C9sN8z93q7CSnM18MfVmGHpW7XNO29FHyn9b\n+w9CFS/G9syvJlaMKJ/BTbO1tshxkXe82l+uEAz9tLwOsiJu5+kqwnjgDXZe\nnoieOrMZCCOLR/YzIbTErLd84oZP761ZiFM+SB799Jh7TWMKJW6AXlANbG2w\n/c9wSvwzfss3wCYZyYHmXQPtZuNkLkot6ZrIpwldEnNodcdA3WLYgVkV35Ky\nN//X0qu9MV+1oLFjh8Zba2LAS9zJwYxG7EGu+YYLK0Ximp5bP1HSt0RC6DSl\nut0Mp5nY8yMMVFoi70Y2urOQVEo0L3BLybbWyjnAFjJxfK4l8Rl1L2eQvC3d\nlJLmNOphaf7FCbiHl+z1XgSwlirgVspSpTaPGZFjRCYX9nYXQXT9jO6NmcHH\n5SyR\r\n=mmnM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRE2TAYaPY/HVaRKKrBNCnfljD9H4fgtcTDEH9VlSe9QIgYCMYqn9Ke3Nl9ohzFVhQMLihPD1jKVcdCjHUbn6Ml0c="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.13_1575567841600_0.5469084587070323"},"_hasShrinkwrap":false},"1.0.0-dev11.14":{"name":"plumier","version":"1.0.0-dev11.14","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.14+feae170","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.14+feae170","@plumier/mongoose":"1.0.0-dev11.14+feae170","@plumier/multipart":"1.0.0-dev11.14+feae170","@plumier/serve-static":"1.0.0-dev11.14+feae170","@plumier/social-login":"1.0.0-dev11.14+feae170","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"feae170e8302c3db06c54ad4f34df84a904aacd8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.14","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-QTVh9l5qtt/WpS1LQ6QRaqgdzAfplXWidapgnM2qVRJoR3lsFstm3QxoYQKbVmXwUfJmmfjZf6qwDxzWEysUgw==","shasum":"53ff3f5c082793adc332d3d99d9ec971ae9dfe2b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.14.tgz","fileCount":9,"unpackedSize":20155,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6Z4tCRA9TVsSAnZWagAAOqMP/0L1IS8YWfYl8eoXRJXW\n5z/vAq/M1JF6G6FiYMUaUk3yfUhmmuxVw9LkyiQYld/HnxhNXFfMcWejQ+WE\nuclSkEJHT0Gme7q8T/JI4ITKGMAPWxyo3zrIXz6Bogk4XLz6o76l8j5IJ/h5\n+0SVEvM3WhoSUrUf9LzXXimG87aHHfogLi1wpf1n7yjf15ZAawKAXxhH/mDO\nEY+n7g01N6D6QRYeLrY0Ips59Hd/38fTXEiu42hnQyRjBCcKCIrA1xh43uTg\n6Ckg8EQ1zqY6oH3nqR6QAf2PBK5hvfEy9nbgzQgRN3lAJj+L/GPOHcRfdQ9w\nd+Shi4TYaKVNZl5/Luyrjxf8MYRooN5nvbFel8XcJ1T9lsJgZF8OCGBfzwXZ\nR8CRVrTfdD1T17C9+5zVCLsc7P4/zidf2zWfviqPNx1/PdkyDpMHNQooyueG\nXb/BHn6k7DayRCsT3wKRJTI5oqChnXbraUaPllstdS5wH0WtScifMowdUndQ\ncxA3fR2y0By6QVRXGhSV8MHMVK/v4ip17jYBsf9yFDKZ0DVjlOClYdqyORM8\npajWvDKiMbwanRWdg6ciFlDBlC+HoJ4A3gV1f+JudNDuXMOovOdAnzBL/h5F\n12WtgRYEcnZPl/K9Zv5A4K/Ud6a0ucYF0WxOATZ4m2TGPwn4SnmVdEKJraDv\nRloo\r\n=B73Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEUdBD6qW3TTc/tMMyHcJ02u2DOpA8y7VQAwb31rSX2YAiEA7hF3o8Kk8KoKh7CnokqZa0FE8S4nTHt1Dm3PdNAGak4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.14_1575591468752_0.2846909323820308"},"_hasShrinkwrap":false},"1.0.0-dev11.15":{"name":"plumier","version":"1.0.0-dev11.15","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.15+7313eb2","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.15+7313eb2","@plumier/mongoose":"1.0.0-dev11.15+7313eb2","@plumier/multipart":"1.0.0-dev11.15+7313eb2","@plumier/serve-static":"1.0.0-dev11.15+7313eb2","@plumier/social-login":"1.0.0-dev11.15+7313eb2","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7313eb26d1992e5ffa25402fce8230f05e79743b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.15","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-Fg3SQb8mTxqxP8CPQxVXfG24d+IiLRsHP8zPKcA4SO97ppYiL9D0Oojf46sMRvSdZjvWqgYJjzA3oOsr7OCpNg==","shasum":"b028a051cbb11de6cf22450ea9dfca9fc995015e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.15.tgz","fileCount":9,"unpackedSize":20172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd7ueOCRA9TVsSAnZWagAAHHoQAJNz12R4tPW3bP/LYo//\nl3+QtiyS+sHokUgLxYJPRe/r9Sz6JkhPuJRW7w8k9EVcgir8zbjz67Tgj/Mo\nSjwzwEDMegC0IkMV/hRUMjQLtVBvCfeDdT/scnDS2Vrtg9HNUDWIDz+29iGW\npghxs6VNRSzXU3ueRdnaXN08sMRGiC8mAluDLB/SSGoIABm1i9cSRk5msMGV\neWsgmRHNeONTuhwbp3hvT6aVxKHyR3a4gWGNBRoCm2dEcF2ODJOMGnoLzJit\nyFPfI9xxQdBGhDbvG6LHGy0guq7b+aEN+KiiqLPuiYziRMl7I2hlBOTX+P4y\nIhwiLHdZEa0jbR3UF+D9h1S/4eYfkNeKI1PoczkNXUd0hHh9wBIiohdLHntC\ny1xUKGtioxToyWJMusiPdPrjWY+zdys/ll7YJ1lwHw4JE1zft7GP1U37k9xh\nrv3hJ8ory/oaaJbtP4ecoB6qE4Ucjplz9ZAnce8YF0I7Mq8pTV2raUC+ZKl+\nghmJkGaRSxeG2fSyByd/HzOXxGD6MGWKyZ/0blGPqqO+aEN1ROEPyxE0Z2wO\nA+F7OPHjQ5SFkQi2T/6ZmvetRxKPmtnjWDH0mPO+UB1I6Klv7IEhMNv5cW0a\n4/Hw/IueAYDaXHokCFv0MS8DDouU0JIZfQsCbIYYQoEWP4mEMFomG2fRiDh4\nL1kV\r\n=ZOt9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7eXOnREA80TrqZ2SpvRt5VosZhTobYWGDAloSIfGQDAIhAIPFLw90+Ml2riIWQYlAZ1e3O9D1Xu9o9BQVUSwz6zDS"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.15_1575937933596_0.6345648853827672"},"_hasShrinkwrap":false},"1.0.0-dev11.16":{"name":"plumier","version":"1.0.0-dev11.16","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.16+b61738e","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.16+b61738e","@plumier/mongoose":"1.0.0-dev11.16+b61738e","@plumier/multipart":"1.0.0-dev11.16+b61738e","@plumier/serve-static":"1.0.0-dev11.16+b61738e","@plumier/social-login":"1.0.0-dev11.16+b61738e","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b61738e1133d71a8e91d09778e0233b5052a28db","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.16","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-ZGNwWMzApRfmlQwzY+vehG9zex1uIpaTj26nOVCPBEn1af0ZVmbZ5ECqnlbyPbpCoYsX+5LON00GdNLXhhLq6Q==","shasum":"ff6e5c8c2396fe26227c0d78ac3ca06ef4c60477","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.16.tgz","fileCount":9,"unpackedSize":20172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd8BMRCRA9TVsSAnZWagAARHoP/1a+xHDT9FRiATodZWkk\nRPpORPp4bUHPQsSjRogF/Gpi1TEZpyGGkQiw+ChH4Q0prhZqYhA+8in2n/WT\nh0+WyLQ1kwCEYn6GZfmAhaPnBsmTOZMF36ncSOeNRp66vJy44880zNKq8tPR\nAHY4GBvjZPY9srUPNqxOZT7lcZb4DWXlIvIHEgi1PFGBEqGhRyYNPcv+w7L7\nRc9//Wc8cVoF6h0OH0jHMqIeQIhTHmih8G3kSUh82jh1SIpFfhBsmVhIciOE\n3ddgFI4dy8ja05l9sEmwiVsA22yqhbHzqqFlbn27QyEuuRVxpFijtHBzWUDC\nNMYAZZG/mv7v8qAKaL444XBQHvgDmJ4uZSvdtQpxPt+lILNgAKhukZKkh1g/\nc9NAhBRgmlE86le/ieIajtpTXJ1y8aLj5w9yFZSopuKczEHfpMT1M6pRsbAv\naBwLhH9KU1n1jB5sWN7OC5yuzZ6yWC1rzD3NjmsSJeukeOv6TWfPQYGLQTh8\n5Ane8d4LsVGc+rTRSDqDnJ97cmyjwXb/RwX15qphI8ZU//+XvP2KleJZuSOQ\nkYoYykrfAOfrUoPIFOFvc9+JGP4kvqpd2+s1nzObWyuDot1UBoS7t52IAw6p\n7Olaa+/rEKdOx3hKBnBarJw9LcWZl1ozFQqpHQ6dUBsShDldAgP6DISH+7Ee\nExYM\r\n=rmbl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDIJ60n2MrRbcOdlpe2yq64CtdLx96fhdjaYTAljbnwZgIhAORJcIVDHAUn0hYuUlW4RceMyzLrsEjpMLqCH+uUdOgn"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.16_1576014609509_0.21289758830780126"},"_hasShrinkwrap":false},"1.0.0-dev11.18":{"name":"plumier","version":"1.0.0-dev11.18","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.18+fa1ae0d","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.18+fa1ae0d","@plumier/mongoose":"1.0.0-dev11.18+fa1ae0d","@plumier/multipart":"1.0.0-dev11.18+fa1ae0d","@plumier/serve-static":"1.0.0-dev11.18+fa1ae0d","@plumier/social-login":"1.0.0-dev11.18+fa1ae0d","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fa1ae0dccebcee3f3ffb2756c324eaa4ec9bf1f8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.18","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-YVBbldJwOXkIJSlsv/59l2Y2JRb/4ezLszJSinNqGbRjX+qyMoFykd0OMpX1xp9JqO/ts1RhJvxrByOlPglr1A==","shasum":"ccdaf509c77469adfa303740dae491c3a4ec5f64","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.18.tgz","fileCount":9,"unpackedSize":20172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd8ytiCRA9TVsSAnZWagAAlG4P/RcGnYlOlzDGZFmpAPkl\nG3nsUM5EXhWC5Q9zDdg3T6794AoNjPnOaySbJFmgxPi/DwFpd6iJcRHn7qVH\nrODz6/CW5c3k/ophvimwsUTm+Rv0KtzED3zKXfnB1VXfdKxDGOEIgnGX2aA9\nexp3SyzOY+op/cSQbgGXgNDi3yAFeFttD2EwRl8BuEY0Masxl4+blJ1fWk+V\nmFe8dQ/5uG4tLHStx1gHYV5Mj7m4mhMDhh6cX97Inlj8V27iAIWy2EdW5gGF\nQlrN2OHh2dF5OrZzKirjSZufUM78fLn+PSjM3r5R/DyUpdCxS8ZclODr+jEi\n7OHTe2ULpy+LEiTRWOcsDnBKULQ0pGD4ivVSJrMNrNY5GZC9l0TDD237P0vI\nAVsGik/sU6Ybkexbq+CjFFoaOk2qqZNL3GsBD3vuPnoTOKLZq1qqsV/OaP2P\nfEUPVDtTDcUHs2RCOTUQHq3GvAGvQRf1f7iYxyb8Hn1G1oRjn2SIeal7JDxJ\nnd7x/5fJMLhsEjffv1izAFMX8umOd5k3Eqe3foedkG6HpWP8+Pn0OS97xLvH\ny612Hs6Ysp+I+uQS6y/m2W4zqoccdLtfZ4cN4FkJr1Bgr4p/wFmwcQ27yIQG\nNvyg/nLbt+QgdEn91F2F4n43z5+zVjnrjKqyXvu1hLYG8twwOjT4tn6aNHoV\n6bvL\r\n=T3bQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD8IGvys2CKCDaoB799IQgkuFgVsfbBPRvZRzdcuWZzcgIgc59IiD1OWkTLuU05mk7fiMi1LqzliHBq9ngA66c6vGw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.18_1576217442305_0.704370920767444"},"_hasShrinkwrap":false},"1.0.0-dev11.19":{"name":"plumier","version":"1.0.0-dev11.19","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.19+66a2aa4","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.19+66a2aa4","@plumier/mongoose":"1.0.0-dev11.19+66a2aa4","@plumier/multipart":"1.0.0-dev11.19+66a2aa4","@plumier/serve-static":"1.0.0-dev11.19+66a2aa4","@plumier/social-login":"1.0.0-dev11.19+66a2aa4","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"66a2aa4dc03598ad8dab42383090a1f9c7c38af6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.19","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-fhsPFXBRU8S5RRB9JWRhGon+MYwzKHTPfC8d8J9yXMS3XcjKGaB9LL/nBvhakRfqfirr1/XcxXhRFcXgvZD16Q==","shasum":"11159032d9adf0a233876d528bc09553cdbd88ef","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.19.tgz","fileCount":9,"unpackedSize":20449,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd80MvCRA9TVsSAnZWagAAtUoQAKCCaX2y7v5atU5ceyv/\n/r5/bI4B8isCoG+TJXm2/ot53ZkXZU8Mm9DeXNNc7f9t/N3DHRxCMrnjU55u\nAqsW+aqk1p3du6GfHC+yY0xgMWcNzifNGhAFefhMqfNN3kua7jS6t/7RNGe6\nZvRqPiYj3F4UI3EffzICc0LbxsjNz0lviVbiPIwBhRtAU6DixQndHdYTnCPa\nM4qWtHACjReyLmHcM6KR5Uym/t+SEwOCcuCSV0zj54Wb6sOgjRABiYhuTd0V\n2qOLwl4WGVkjiVHyiKOdeoVkUEzgaoDCluZkldk5xRK9bZiOe2/CW74i3t5G\n08AysdNnUV6pvdYVrnV3ZMD0e8opFrMa3AO2MIN338IspHHE29cNsMDV54Xy\n1XQplcYG1ojkUKeO+fWiucfOTEVfQ2F2Bh/cVvXo7vr2RvVqayYZva5zp3tH\nIWAxS/28qbBomRZrAP6v1f4kBKv7LAFe7KL9E+K1JBWgxIMXW7vnP+JkmKI5\n3Py8PWSID6gUvVR3eH/qoJsyBQ8ZY21w5MT7QcPX8WwM2hCyUVU/t9WnQcCe\n/PSueuLYOBssz6qIHDtQIc7dAGPYGUq2EeiYMR3Ssmg1KicZpU+AWiHMbDHn\neWC9Je5TelyMA1pVxMACPDGWKqaCUD3QiI9VJGhpYuFzVRp4ygOZf2Y4OCT1\nNHrH\r\n=QQ/P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD+He50vwgQTz8EfZKZ3xmUU64DKRYmtMxcrrePoov0nQIhAOt6xncXQLjxu611blpZY3OsrpejZpRYuJYL7l+PnhCA"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.19_1576223534450_0.0957528880303471"},"_hasShrinkwrap":false},"1.0.0-dev11.20":{"name":"plumier","version":"1.0.0-dev11.20","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.20+79ef47e","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.20+79ef47e","@plumier/mongoose":"1.0.0-dev11.20+79ef47e","@plumier/multipart":"1.0.0-dev11.20+79ef47e","@plumier/serve-static":"1.0.0-dev11.20+79ef47e","@plumier/social-login":"1.0.0-dev11.20+79ef47e","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"79ef47ef555a8f22e51daaa0563ecba4b6891466","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.20","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-9uqbqPKwF7kBhlzL07xK/wiQJvqRH2ziQq/yRgZF/n5zjzjIELe8F5vtpH9v6rB8/fitGtYci9r2Ka0Fjpi2Hw==","shasum":"46a324d0d56542cd57ba39c6642c784b4f778eb3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.20.tgz","fileCount":9,"unpackedSize":20632,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd83RtCRA9TVsSAnZWagAA6IkQAJjJKHvm192y+GzvMBjU\njydkr0g10zQ9YB7aBHoQ3+GP40Hthx5b5FFLNj5qdJFnRbok3lC4KEd56jKv\nszSuXSZg/3ac80ifHdzGfrPK+T0NzbgHrQh9Kk4/1BTGkLC+jbKjGdl08Olv\nPVrKAqX2z/WLOptCgjqJvpJx0t52PJ6faisNB3tNmxLQgYNafDeHfku/wLHf\nLGp58Rns55/V23v6SULKSxhVF3eJhyCpTAtPZKqeOuF42IxQf5L98X4ViOsA\nWz6DLogauMPV7bDjEIcMDERHfIcoV4G3XOAc4d7IhAU9TVjtinzdkTam3ox8\nzVQp6iYYnKtSKtJRBVcAc/IZ7dZw3k0rEPlafzDLo1Rt5H0t0aXPT0EnYDXW\nOPLKpDNZ3Q8XzsetE2WOfIyIBU3YH6u8O9bhZEvE3hGG/gLjcXv7upecJ9cz\nwkXtix8HW48pZ7GZyGj424AM1nD9Mjs/IH54tv5iDbQV87A2f7Nmvsg1s7zR\nK8Xd2JuP/mMa48RSatAV8adCn5cSXR3cjLqc0BOzvdCZpOqZvctJa+bZgnuI\nxdma3sVHQomh1L33GffXajeDZ5HThbRgtoz9osj4bdi8TjT7kM5nqWqD1eXH\nGMt240btqA56U0bN3XLyIAnrXGQOCzzOn5YG0J1css4skUzWvCQd1Pnmt2lP\nJUZP\r\n=f10p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDU32N1OHlvGCmvUeqQNjlH1FxuwZcW/EKJTEXwqbuaPQIhAMrFGf+hpEUdQFxvM49ICRsMWwoyr45f7Zu88b2kVaik"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.20_1576236141092_0.5861794622699155"},"_hasShrinkwrap":false},"1.0.0-dev11.21":{"name":"plumier","version":"1.0.0-dev11.21","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.21+2c44ddd","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.21+2c44ddd","@plumier/mongoose":"1.0.0-dev11.21+2c44ddd","@plumier/multipart":"1.0.0-dev11.21+2c44ddd","@plumier/serve-static":"1.0.0-dev11.21+2c44ddd","@plumier/social-login":"1.0.0-dev11.21+2c44ddd","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2c44ddd750d88e306eba17e5fa530b39b6879fee","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.21","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-Bto7w+vRt25XDq0R+dasXBsKunlJDBvk8se7l9xb7BjyAJ+9XoeEVwTW+6kDXP8oIG6y3BpGckVm3OXmz9npOw==","shasum":"e2e319207f672f44c12124d5f97197ce5dd55d2a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.21.tgz","fileCount":9,"unpackedSize":20632,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd9A1CCRA9TVsSAnZWagAAotgP/R0C8jiKk+M2kQFsXHCO\nN4k5FeSSsD7ibNj96cgTpHcUuzJL+5dLfUc13sPtxorb2ZzInXoal9ISsLe9\nl3YQApcoWP8b0PHByYGtmN1nbFQneulxvfTMkzvtjvviWmeKcx9gGFTRBt3N\n8jcHgrRfwlzVlx5NygYITbBRP84zf6R6hKxy43A6YZPE+kErpjTdLRbVULGH\nqd3evZBapfDMPj4zPiw1ReVpAYSGT59+I/nTroEqiIWpBqfN/SgO8EwRrpF2\nf2x9i3Z52P/+mIZzRnp5GvGgORM5Fhplhn2/1FRJACHXsfk+1Y0b2KVUVcIj\nmxC5VdeqN6e2qebjPa4PGA/0Qkpu205vyMxdDDdqxRCyp9QWfkTzmm5NTT4p\nXs2Vxx8sOFF8nuCZNA8RDoD88zS1RS+wuWfo/L65wB5HCVTjNOYv1RvsK7H8\nGZPd2igPNqfV4+2/W1RGVniLHPI3hGmftlJcYgm9AvvNyo1KfC2hPNAFL/od\nlsMbQ05HaY0CYCethtQ9MhPn0rAv7URo7jUt0AXDtqBUZrA7KS6L+gCXbDzn\nlmwd6WoTafthTkk6m8VwUrWQNzjJ3/o/YOGmBL5e7pkG3yarGJ8TzHJEP/f6\nR2Cul5/URFOWc30fMMyn1iChF193f/56elyVt4uxEvly3Fjc8jbMBXym4FXD\n9uSZ\r\n=u2QD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID41ms2CfsJhFAgK7fNlVZSO/oKZTBt/VMiAXV48uBg+AiAQv/alyvhyNHvrBeGRiQFDYdJYdNV3BdQDOn27MAREaw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.21_1576275266230_0.3225059875905987"},"_hasShrinkwrap":false},"1.0.0-dev11.22":{"name":"plumier","version":"1.0.0-dev11.22","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.22+b521953","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.22+b521953","@plumier/mongoose":"1.0.0-dev11.22+b521953","@plumier/multipart":"1.0.0-dev11.22+b521953","@plumier/serve-static":"1.0.0-dev11.22+b521953","@plumier/social-login":"1.0.0-dev11.22+b521953","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b521953e50500ba127bbff0c6761fc447ccd3003","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.22","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-s4zDiLo141Y27HNKq1XyETS/t3/oYn9bNwm0Hf8YaCdRIwpn3cn+tadXaMbeK7TFbqwPyYJ3P/+ucT1BKrj9pA==","shasum":"9f65370a2a88efdaff3500c12f7551a29faf404a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.22.tgz","fileCount":9,"unpackedSize":20632,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd9Y5dCRA9TVsSAnZWagAAmXYP/j8eeiqU6NNkD09OVc+B\nh1qOnZKaOBNblCDY/OB5FrlBwW2XPirz/Hdi5dgvozrbPILFLYDz84M5H4Di\ng0KHyfqRbtB8xl/ugJ/l1RbXZ8MmrXx0ucuinANGktMcxb6kMj3F6Y8VQJRa\nQOP8rKKHK+Z5PQzUA3nu9+EBl9CTgwtiSRfBGIYWVOImYJk9Qv718p3mcy7A\nQePtfPuo0/DC3PfCzVazZb4J+OASeW8WkL5dZf8U0uNYct6ToJnwxEe7A9E1\nHk3vuVmjitiU8AjaqYapSng2N7cPvkPypL3sZtnhOh/u74kI4m4iJyweDCmd\npxSESsNOMqUVWTwPURYPl5Tg7FcOfKjL9h1PUNoOobaG6/qiz2XEmesWS3xS\nbgTuLTuku+92qUSVFDP0rPzurCnbg3HHKuUbhitD2+wIXg2l9D5/gIvYOqd+\nAQ+/Vs5tUIh3leLFRBlUchafjGGNiq6zMo0JuIGkmFAT7NkteWyLOEei+fmp\nyV02pnqp5U33q/XQX7+fo1Qzs4gO3es1F5c4fc7ER8NrPcm77s43/qMY5iWl\nqciW4REE1NPI/MTQu2lirMnl2kGfbGbKzf6XpM4gtrG/Yc/2qp3WjtN0e0fi\naBULDlL39caaB3GOtz3ZMbjYU6+1OLjrQvgyNtY0/AJaOUqBoIE2OvR2ffLY\nrmbX\r\n=oW2e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPBWthEBCZbL5T9F17xB++MQ3QjoYPRSThe0u/VUlpzQIgLZx3bRSxLfGu9/iCofE93RhDslrLlTziwSZT5hAqCnw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.22_1576373852759_0.04360784221650027"},"_hasShrinkwrap":false},"1.0.0-dev11.23":{"name":"plumier","version":"1.0.0-dev11.23","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.23+9149760","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.23+9149760","@plumier/mongoose":"1.0.0-dev11.23+9149760","@plumier/multipart":"1.0.0-dev11.23+9149760","@plumier/serve-static":"1.0.0-dev11.23+9149760","@plumier/social-login":"1.0.0-dev11.23+9149760","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"914976001fd09b75525ddb1ba104371bb5147fa9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.23","_nodeVersion":"12.13.1","_npmVersion":"lerna/3.19.0/node@v12.13.1+x64 (linux)","dist":{"integrity":"sha512-OAU14o16TCJjsgAJqzEQicBd/cnOvHKpklOeaaTReIOeovG4qlBvzFdZWZ7GIMk/26BL6UULns8tMoukD0YgIw==","shasum":"8239a925f41f279caa67a792a543c0185ef23068","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.23.tgz","fileCount":9,"unpackedSize":20632,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd9xWUCRA9TVsSAnZWagAAxdEP+gKhqYYCog1QaZzfLTk4\nP14EhHH3GFZEJ7djrU5QXcNgstYNmsIopH35L4oRvosub7X9A8mKZkiNv7O6\nK9OsXNU5stwFXcLWCQRDcwy1ozaavjppC98m8m37Dpg4hZdyBZ+DDBBcx9jE\nCmhudEmwgiRuhA/Zb3of+DQ9nD4mnau5JFNuzZ+Cma/h9eSu9A/gqwiUa63t\nb3vgGEt6DZNbmGsrPP6R6Sg7xgD3JueZsypJsNypkPyZ3/KjWxdWM7ORshbL\ncQvWKRATZ2M27xqhv6Hy00xCn9Eu3Dc8SFJ8loy96A8swDWMtceF0qpMbgbS\n4v26idZJjjKhhg5GSo11g5ojhmnBAxW7w5wOO3wx+OLpAq73XsKjnqswoAEa\n5czM1ZC2AAazPkozZx0ZcinK9kCvcs7l3kAXNpVBZu09wuFZITLTuu9csIXs\nNiI41Jm/HfFzpKWRsVN651fc0ZI0I//MwG6EkZH+g9YwOiElFZyKpjHLF1IQ\nNcNlL6sfbae3bdbrl/C5zgv9yoWkmKC92zUfZeudJ7F6KaDtpILHHiqb652k\nbDpBfDEqw4TyTDZm7GW7k06pQ8sHyVKkomqrJClaNX4tgeJrbqNfy6JJbuFG\nzMWG5jWwEIc1rqIJqBHNxX0hZXl6S6GPtCODWXCiqpiL5+FbuJ8hXnLpcK8G\nEwNE\r\n=q4Dn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLN4jkxVLE359vpQNf9OvdbqeKEZwCVBaYKdPlLX0A0gIhAMC1GB8k36iYXGWUdaXQHUQ0V/J1aZa8rYSv2lp0Zw8V"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.23_1576474004128_0.9589597438173914"},"_hasShrinkwrap":false},"1.0.0-dev11.24":{"name":"plumier","version":"1.0.0-dev11.24","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.24+30f451f","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.24+30f451f","@plumier/mongoose":"1.0.0-dev11.24+30f451f","@plumier/multipart":"1.0.0-dev11.24+30f451f","@plumier/serve-static":"1.0.0-dev11.24+30f451f","@plumier/social-login":"1.0.0-dev11.24+30f451f","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"30f451faf86d4539d69f2ef31981e2793fdb9337","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.24","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-y6jV+mSiV8k8/gbaCKYnb5wB95638ORX3KmmLnNAz9Q0L9bhZZZE2fiIaW8o8rfXnGtgU+p52ebrOI2I5CbR8w==","shasum":"7c7c15ee0f94d128b07ad26a789db25529144354","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.24.tgz","fileCount":9,"unpackedSize":20761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd+eygCRA9TVsSAnZWagAAzwMP/RXs5bh3gZ0pRhAsT5GY\nIU+zTizbBRknGeljy+hS/6IqFKNhpCle0Lnf0ATE1HSf9LiYFFDw4d0VtSe+\n9+ZAXgGpDp2UqNRRabdJptgNCFXQTUkBbSp2ZD2MfupVJb35M8aAIlkbQVD3\nHlclntcOk+SSm7R6uOUT03U6CebGKNmklBZmvPkMUlxOpgCuC68Mfvt7qlRU\nNjjBXVJb74kDTRZoyNItCScypeGLZm0SIpFziyVwqqs8FtiKunzTI42sKoJu\nvApMm6tVeYH1M6x/SO8EWDedpvGNONt8wCnXNiuTxyQRa0gNp7m74bzOKU7p\nHQ66Wlc2VXNn9naFWe36KFOqUSO9/VbSnnwvupyJJNCG4c01iL04fT6wMfB3\nuh4gWU/ue15/wCuaHFdO7IcXpPI0Rx6VQlna3dpulZT2AK7UZAcy9rhW3scj\nz74p1wmqQmOiAzBd18eYEYE7GyU/xqgx4Q36gc1iw0mXP6MPK5Uz2wLpP+8x\nH+mgl/fai/DfzJVkWSTFftCMVJhPZzMZTsgku7xSKDRbQW8ckgYpIRSY4Lnm\nn0n/b0oiYc/mIbXABjDu/d8dNKialzaw9J/css6oMJgPTYS7jXSjszoCltGZ\nk/SYQja/h0T9z/hSRykzN+Dy8QKnL2LnlK4ZrOSSW3IIQYMCesaOtlfK6Xeq\niHB7\r\n=BK5l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGq+B48CTVqfmR28zAOY6xQXG5m5IsnXvvOZZhtARiAEAiEArfHIELDXJsbgO5+mk76+56/6DfF4IUBPEe5Jxf/ch4o="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.24_1576660128633_0.23442723838875534"},"_hasShrinkwrap":false},"1.0.0-dev11.25":{"name":"plumier","version":"1.0.0-dev11.25","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.25+b387624","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.25+b387624","@plumier/mongoose":"1.0.0-dev11.25+b387624","@plumier/multipart":"1.0.0-dev11.25+b387624","@plumier/serve-static":"1.0.0-dev11.25+b387624","@plumier/social-login":"1.0.0-dev11.25+b387624","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b387624fbabf3e089e420f475b97607c2fd99acb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.25","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-J6nZPhLCoQaOaToxt2sTUjXCtWbkvSdVV1bQvSa2+LjzN8eEHltKpyWZoNlA9CFOjTrVM2krvPySg6zvEknKSw==","shasum":"0fee5b4532d366dbaf661fba696da7019ccee0d2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.25.tgz","fileCount":9,"unpackedSize":20761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeAq5oCRA9TVsSAnZWagAAGnsP/3XkgFaKTK2ATz+h7hB2\nEoktEpPIv7fc548exzbWJ9HCXesdJ2OmGObosQCP38eHqmtuEHY/qCioRZjR\naixHn0im8gR+raFe4tUSwv56ozujjaDwZUBVC87M5oQ/t2m884idYx3xF1vy\nvXvF6NYWZy8lhEuWUF3DjaIOX1ftUHnhDkhYGareyzmqnVHZAgEIgqu1/qJb\nSwHhO9Y0tktD6vAaMCqVJGw8+Wip7du1GKtkcc6Rm7TiQJjabmKIZAlrxOF1\nLwarbHpwCHbU4ExITZB6JlTS6uIKHe9Y9/c7PFl3AeszYIxzGcz5BWDm8Hwk\nqh1w52vwmy5Nfsho9U3BGjqafWk7BrhO6HSbst604cbH/jyMDJLKMSjHGFdc\niZ22F5zUMVB8IUcPTaMRseeHxXOem+pdiPABbfFdNPsaQtxP4DX+q0ZffJBd\nPa5UGt9DjSyBaZFIGL0C7XNCi1oOp/mYgBollYD2cXOFwloRnFDQr0VFSiGk\n0c/7UUlL2oARoOTk00ougTalZAot1BWc0cPJrDl91GKqRyM2xlHA1HIu3TPJ\nNT+nmKm/s10GyYUOrZUQHBmVEsOEb4yx7GyEgoZ2h/elNFkMiQquVEnnAAUU\njuOi+CRqXvLqNUemNYSvAGGWYTEdeDoie4l23aXMgRkkHNvrF4SW38r2hw05\narn2\r\n=bnGg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID0fozEW2EGt4nAvI10JhBLE1E0JLd78aZfyVnXUOlQXAiEA4UwGY1OvvFHP/Ax+1mT1wA5UA1j1oKjzrXW7lUff/h8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.25_1577234023933_0.16760217376003506"},"_hasShrinkwrap":false},"1.0.0-dev11.26":{"name":"plumier","version":"1.0.0-dev11.26","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.26+762d72f","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.26+762d72f","@plumier/mongoose":"1.0.0-dev11.26+762d72f","@plumier/multipart":"1.0.0-dev11.26+762d72f","@plumier/serve-static":"1.0.0-dev11.26+762d72f","@plumier/social-login":"1.0.0-dev11.26+762d72f","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"762d72f22440a22c8f4c553a0842702af601054e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.26","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-+XlofmpYj7+aT7cx/lD1G4LXL1WaGeaQkKujXo+lt1c5+e8nT7J79qPaTUXMayKRVqLUdsihKCwAJZ3xKeDy7A==","shasum":"c21d63b132a497a445e2756d875ca8d11234f136","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.26.tgz","fileCount":9,"unpackedSize":20761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeArQ+CRA9TVsSAnZWagAA5FIP/03YKtrmD17S5ZIzMWtU\nemCRvYJ1gK+qG8Ns1jbInecMi2xlMnhSiyhgW0jKPuvE8Wc5XIhE7Mm0MNAI\nJRJrLpMakshftN0rTmxg9WNBTSFYuiIKPwUCam8PAJJ0c93ijqPn+e47n2hp\n3VH4EWYHcrDw+IoAg3nA3BtphtclkLSWJ6OJp/jKuekJrZ2HbgkI/TLg+1JT\nKkFa05So2XRcMrD1jIMvl0N287fw4G42gx6VDDJOq0BO60peKDCv7BDXM8bs\n3VkoGucIQZbO3FJ9jUpcjl5ZOzibhaJWeR2Tpoq3pk8NngdXJ/7KmlLzmmis\nLYUzCa2oHbISvY8ZQeDoTWZ79PmiYtzVGAL00nhKQArQKLyUooK2btCo5zhu\ng4QThU1uaCtn4Z1R7ETAxuP8cwypopZBT01XPVpYa3UrdpIEBonprQSy2X6k\nlbU3AvbdtexWUJH0v9FjPPwdERXxMkxA3rM/KO3DXBvehZ57njo6D0QNTCed\nek9ZK/JdBWqEfOG3Ud67JQUdFePj6lYFCkWWJFcD3jkpXXRPi3CnWOv41Pr6\nnyMBZxkfuC/BAFHyPqo17Lu1ykPEgRUEx2iK78v+VwsUmdSrmXPdT0FlRJNE\n+axR90IQZ992c9dhPpeH5HAfNZH/6/YDDVUdyCUhhuiRAEl1sJPLfn6KCh3y\nr/K9\r\n=vUX4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICeYpM+8V8ihhboLyDyiBLE9ezVTM5+hOkPZ9NH006EAAiEA2dfkej49cx2SnfZ7yrNJBrXY9qXSXdqVpNbOti3t4Ds="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.26_1577235518176_0.5861595072275454"},"_hasShrinkwrap":false},"1.0.0-dev11.27":{"name":"plumier","version":"1.0.0-dev11.27","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-dev11.27+9037a61","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-dev11.27+9037a61","@plumier/mongoose":"1.0.0-dev11.27+9037a61","@plumier/multipart":"1.0.0-dev11.27+9037a61","@plumier/serve-static":"1.0.0-dev11.27+9037a61","@plumier/social-login":"1.0.0-dev11.27+9037a61","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9037a61a7691a738e761669083dd7792f053cf6b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-dev11.27","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-45qN0VDowe/7EPNE9gNPOtrftxvOXhFs93S9lHazGNl6orC7IQpZNW6cxO/Y2FsjNkPM8sjyhB4mvCUVsR7JSg==","shasum":"d0910a8989af7a23eb0cbf1effe97baccc50dd1e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-dev11.27.tgz","fileCount":9,"unpackedSize":20761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeArd6CRA9TVsSAnZWagAAKT4QAKIW9TrmoiH0mpAEPJGr\nhI9fnFUwwnvkV3QjX4jrVcJepgrSPUfl35zQiTYEWNjFhtH9UsiosoaPxecH\nlHEJ1lEmagSH4Dvzt2lYTgpq43cTRu2O6bnmmUY5+Sk6yfg5MV8+iTsiHKZ7\nJDzkROFaGRs/x3MZ5u9XCGgRCqUjRg5gQOM/hFhKp88CZc+AK17YSdWJsGZ/\nK7ZG5F+592MF/rMOZsrgfCfjkCrZDZ93+bgTTm+7rQzXFslHkQqg26qM35oy\n/xE14w+iC1o0vGlfzEWADdNIgkfihNKQIsxqblXLE+vq0UpaqGZtdiMuC9lo\nEY5S1wGKvtpSnpNFFZDYXgnxXIv3T1DnLULByZWa5RocHn9YqTBo0kX2UVjn\nW62g86gJ9bdAR8ExcPu9e0fNyyAwjL5LQjKJFUB00JLwsiAlPg9rc9gGS+T1\n9AWkY/FfikdAMtVVWt6YAb/4boMPsu7acoKoZbUQVLMSuoGFoZ2jMm7/hWzj\nlTW0kA56gXyBNMtPd+d0DMJEs4L5llCo3SE4ClnUZhWnC3TThnZWo5g00sb4\nBdCBKGA6PD1JhOKL76MSbiUmBViKHZqJa1ZvCP0Az6+2TpX4gIulnFKM+T3a\n+OBDDWJNtKyAA2G+8zZRL9sCG53bD0r6HgVdDYOOUvLaJwKD2Uh6e65LikbF\nkIdC\r\n=njpA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFYeX5rhOu96JDWYmXLWEg5+Esm/wg8jqAV78tO3azPmAiEA7Diu9WD7mvFiLcjrQvErq/QAmT0y11lDMSUnXS3A5Ko="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-dev11.27_1577236346257_0.2698132908833857"},"_hasShrinkwrap":false},"1.0.0-rc.0":{"name":"plumier","version":"1.0.0-rc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc.0","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc.0","@plumier/mongoose":"1.0.0-rc.0","@plumier/multipart":"1.0.0-rc.0","@plumier/serve-static":"1.0.0-rc.0","@plumier/social-login":"1.0.0-rc.0","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d67e953e8f7cfa88ddabfbed2215c12a2326fffb","_id":"plumier@1.0.0-rc.0","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.19.0/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-RgMZsu//WckOiLV7qhrwb+KxNRsnqfZzriMpgNewD0tDeNUNt1gnun54hh/l+s1FCXqYGrF/HGaYEK+DiRKMRA==","shasum":"6937605116544858fb4124f8010436a748709d4e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.0.tgz","fileCount":9,"unpackedSize":20677,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeAsGjCRA9TVsSAnZWagAA2EAQAIPvuRxQ8R5IsLXGlVvx\nSADRH28OQ/LpAJA0aFemzxOVjQijsdJcZVQd78q8zSdN42yr+Y3XMng1VGIv\n1vTFK3NDyWDGu6cVXlpwvXjqSb4dhRGt+WBYmysxJJZaxQYLkdNGuuxrPS6E\nWLTRJLkRZSv5We6fpBhVSuMX5/VEekwSyt4PGwtP6jHdr3ka7Db/9wLfqAHR\nF/ga6E8VxQjmjK72PBhFusDtqpEC0hU1ozQjLNvV0Ay4QGasxKIKbQ4XbHYX\nhFSX4g94F2qXuHni/P9Ov3FuNI3GTo7Yg7N4y5tIbkbNpypzzUI/l65uZ+x6\nEHvTO0BCMxaRcckCt+bT1FcVv+wQn19J/sZ8Z88CBy/6Bw8/8i5im/MHDlR0\nfGhk72Fm6X/MPa0v/hnXRAfmeeQdKpVLjGFwyoZTp9uwjV5v9TkjemoyPJum\n0NuwXY+ghnwRuHnhrCotzBWZP9zDlUJCd3abExXbdxDIFlIZ+DXcH9vpUl++\ndlNlvMvdEaQ07gwoPisdST7LGEMeZ/vFibayrcA8TvxT77g3J1nz5D+RW4cK\nHQeQijx7haKNiyyJ2/eVbrcsqT6DaZ4mV4keqoYrUthH5yHwzv7JGG4rw3zT\nN1dRcXQWCHxjH8k9IdvGq8WBySPG/xFMLmEFjQ7sGk1GVa4fJ6OpM2nLtJnu\nzoBU\r\n=exlC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqKPBAvW3Eleb73mSpdrNX6yq9mYGbRUnxYIRQIkz/zwIgcny2+d4Uknxzv6OCKNQVBrKNqo6omQLupicBO2EwlCU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.0_1577238947091_0.046746240834484976"},"_hasShrinkwrap":false},"1.0.0-rc0.0":{"name":"plumier","version":"1.0.0-rc0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.0+fa11c0e","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.0+fa11c0e","@plumier/mongoose":"1.0.0-rc0.0+fa11c0e","@plumier/multipart":"1.0.0-rc0.0+fa11c0e","@plumier/serve-static":"1.0.0-rc0.0+fa11c0e","@plumier/social-login":"1.0.0-rc0.0+fa11c0e","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fa11c0ea3465cae4db59b02ac1dfabff3f73eb59","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.0","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-O3gdeGSr2riRZVjRsjH3ZxEcHJHzPz/4gZPtwslFqCpAouOxfDQJLeViVHsXwgvzaXSi/IBwY/pl++rxu0UTlw==","shasum":"ec8bcc8e293eb2fa90af4572885f4ef7432c893c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.0.tgz","fileCount":9,"unpackedSize":20740,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeAtQfCRA9TVsSAnZWagAAY+4P/2qQ00nyN8vvEoHjnmpO\npNDzXHOQ6MlKOYMDUV6Ey/YW8b5Rs0A56R7CTapE6krAEl/UtzG2g5euAl7j\nx9iwJ/6hC1pGFEmPbbuaAKj607sCUeXK377YP7PGmiu8RzY9ZwaAJJ0VrrG4\n2fvV9eBM4KQDLGWRaqhvRCXb04cGGO3UXAVivV4E6GOSdmFB74bNSC+tz/Of\nDmjEmeeGLra2OQqDBdTrP4zzIewc3jcJtWKNSm2j00719+yGLtSUYrpzk1RN\nH09VmLobf1+qMdMYt8nD5oApkyDFjXv5sJ+DTraadOnjzYghtEhX98ME93tK\n0BC+NB2RnLsW6mcv2CSFzoB+QeL16BhMp2z7jqPa7EtZw9aXzpMOKqIpLiYA\nD0dxe0xlPsEx2vJKvm5fk0tiuyqpOQGFuu8rM0T583lu7QXJNpkJlurMxp8t\nfc72eRMwJEBgL5R3poGYTKElgKs9e4icwr56pc/WLYD4N9/6fJcbzwNd5Qh9\nVchygzb3ENFZXMm/rIZHIhJ2j1ewuNOQX5fq8Ot+O5QUWdif5B7Waia2mXdT\nN9tEdXbgS9sAhYtTXaKVwsH99dnoYxGyXSr3zQrQJQ6ogSOCf+YTWLa9F5la\nvpkwTlUbFvhBc36WTVxiN4Q/4XfB7E8FRqH95l9IcF3H9pcMoTXT/Rm2LNIl\n7C/B\r\n=1uM2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD50PoH0fWKjgV7hualXzbL1rxdUTORa+5oGx4zhGEwWAIhAKZtA1EPmMtCBZhmebFlP7NRrFeQd9Dz/nOyTVE9DNlk"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.0_1577243679518_0.17250830656798088"},"_hasShrinkwrap":false},"1.0.0-rc0.1":{"name":"plumier","version":"1.0.0-rc0.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.1+7ab1d0a","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.1+7ab1d0a","@plumier/mongoose":"1.0.0-rc0.1+7ab1d0a","@plumier/multipart":"1.0.0-rc0.1+7ab1d0a","@plumier/serve-static":"1.0.0-rc0.1+7ab1d0a","@plumier/social-login":"1.0.0-rc0.1+7ab1d0a","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7ab1d0af8e637ec72fb567c51d89e27216d7bb9d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.1","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-/HZFdsfxsK5Z6dsLkE98n4rHPKzdDm4ay7ZOt6RnUC4h3W3hNY2rVPGt7D3GCF3Q50jx8t/T1Wq1sI+rmZGwsQ==","shasum":"7580876025dc9c936ee44568654816f616be7602","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.1.tgz","fileCount":9,"unpackedSize":20740,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeAtxdCRA9TVsSAnZWagAA2pkP/i1e9FDtbcaX/TGbaJ3W\nJYBAYrNf8nutboodhjVN4GRPIs1+dH+oDeJjWLxwhLVDKQaCAJCILnS/pu+b\nD3Bd3epusUg7qzsXxzyacKIbiFGM55wedb8//hfnkwwsUUKZ6NLg8n/Nb1yW\n2trgp0f6tezzs/OBchELRszKywe+I9PMAUI5x+hy8IRMK9PofXTsyVplfJQP\n3jQmocgbo/ZpobZ/VZtHJRqEfzgSyZ+wDAVvx9+KMVTiyWx7GXOjUkcqQTWW\nwggXNR6e2NU++0OqXEuvHzl1+0XHpdEzzsc/mNV3YCiwQCyQjQ/ZZnS7gFEM\ngR6WSr0/bsJAtKJ9O4CM57tkHZGdRt6nP4p0j04z3lA6ygLJ4ys9meBNMSvp\nlPcydj9uYItq0GOS/WGyPsh5vTSexlWwb22kvwvp/OA6HHZc7GlQOBYJm3ST\nFLmEzOeNIR8T8OygSyyMjo5CQ62gBw8FFg/38BKtxpMmOCgexZwQhmHiecDm\nrRC3Df28Oez7tcztGkpUU+2F/tYfILj21qlDcTgQl0bPepqQ4zpbQq2wfKn9\nN6XCLd4qtzF7rhSli9X7LHA1rELKiCvGLZF3xDMdI7GHiAS0AmcVHlGeOINg\nHgJpRZGnt7CBo4fmy5qcBV6/oVhTQSAfg4FZwJ5jNPVjzL94O0qnNkksozC/\nIYJ4\r\n=jL0D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIExkOpdA5C9XMolL3Et7dBTPSeDx5qzDamN0i3Dub6R1AiAU5WS0M1uShYGYAb9CYJbXTu9pWBUfUi6MYL+QVoeaDg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.1_1577245788677_0.5483488471103768"},"_hasShrinkwrap":false},"1.0.0-rc0.2":{"name":"plumier","version":"1.0.0-rc0.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.2+a5ac982","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.2+a5ac982","@plumier/mongoose":"1.0.0-rc0.2+a5ac982","@plumier/multipart":"1.0.0-rc0.2+a5ac982","@plumier/serve-static":"1.0.0-rc0.2+a5ac982","@plumier/social-login":"1.0.0-rc0.2+a5ac982","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a5ac98215b3a832d71f5a7d467fda1cccd00eafc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.2","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-9uM5XYwqD3wtomjFLVqw46e4JeDlXbC3tPOgcQY6s9PCh4qthT7a5X+Jfbz9jYE3wbgNvjUa4PHH41TmGZTkfw==","shasum":"07f65516841754182424a0a76e97ca7bdde1de85","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.2.tgz","fileCount":9,"unpackedSize":20896,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeCprpCRA9TVsSAnZWagAADeMQAJybVDQ71Ik1ms+Eksua\nirVn8BRIPSB5Tfdfa6njkNSax5OoPtjtP4I7zNOkhQyr5rZmMYYnPfFLe4dh\n1FlL1aNDD6B7KUdBpjI4SlRZ4JW3pqbMhAiqmFFgY6/N7iSMx6yJt/a+hWY9\n01wZgAsf+MWaot6iAbPWqtO0OkbJ0LAPMemXdrwsJ932YG0e1H03mzfMie9+\nQc71yXjuRMCFQJwbAYEJmoOIpfHqTJyCLimsb3i1wGVaq4S+Gfggfb94222O\nDGKbw+5gs/XasXdNQERiLr8yJbZXm3oS0/eRcCeqxaSb4fQolJJHhtgSTacP\nDt1WCGLRi/2uqxWRKcCiLnJYJa0k1B+vPJnvT7YbALBf2UeHPZ0PRafDtw6s\nWOMDCjsvvqn7UZc4rW8ZlOhfR9Tzu8YsHzDI1CYIq3rQiWsBYX05Jf5v6QHS\nlOnvoCPBmMydx+gAEMhag+xX83lXtFERAXkhRU/rkFn5k+iKPPBX9yFz2gRz\n9vRu4oUs6MEjv+tb2oPJ0ykJ6BV/35u4Z92ztsNjDPMAFoY+DXkyzyNDk47k\nadntufuBOq8+oYLfbD/6zOTLZwURyKalamt+9kTPL614ef//qJfvxbtpdY0Z\nYuoP9sdclC/QKa7lYM3alI9JmPONjlIj4F09jJzPN+or2k+WC+lon6lGldWD\nMcA2\r\n=PuiF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDI7vI/GhallpbTvjpalfR66QQ6cW2GOW1VutTGCD5IBQIgVA68eTwW6/TNrV1rxzSoqu68MUpB0MgkgEfP4nSsxn8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.2_1577753321318_0.6303912440158015"},"_hasShrinkwrap":false},"1.0.0-rc0.3":{"name":"plumier","version":"1.0.0-rc0.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.3+6530c46","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.3+6530c46","@plumier/mongoose":"1.0.0-rc0.3+6530c46","@plumier/multipart":"1.0.0-rc0.3+6530c46","@plumier/serve-static":"1.0.0-rc0.3+6530c46","@plumier/social-login":"1.0.0-rc0.3+6530c46","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6530c462016d18105ee1e8580eac1dcc29e91f75","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.3","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-oXvVcwBckrmsmZBteh/cvGBgnCTBnLJcG8pUz2KPyjqvLVmdiudcOzw0SLwNB3r3sPVmUIojBLlGp8VQJdAxJw==","shasum":"828c19e2eb992729f391683b9726eaf20ac7115a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.3.tgz","fileCount":9,"unpackedSize":20896,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeCqeeCRA9TVsSAnZWagAAdRkP/AnuN/K/quMxvvvFW3ct\nDnLhMwiLbmZi/3XLls5OKpfKolVbkJ6AAEbg44kHWoa34i2imZ+L/UmFye3z\nCKNW0U5fJFn4+FItmSXGP06VHUScgDCcHMUbdpZo98OG52wb3FH7B0PpO+tU\njcHVFvcuuxY2mx0MnjP2251JcVNgqaspy2/dY+MK8fI8zd0dk2MrUKkHNlyR\nwa1UaLJ5Ix7Bgcx/w6ZfQFCfw6lXq7se/xwj3+8wLklGzk+PY0m9Eq5bnW9T\nRfcqwKR2ZYUMpynyYxtQozAqS1ji+KFqlba25qDO4M/kR0xR7aEUEGZ433hY\noKa0oywefuUWVZwpdV/uNdhsppOxCYE80NLbzAdynBdNV3vHbxc1+jEg1N7U\nzPp7n/rOJleMQdN1ravlN24wjsyDHlLHOVQYAtwcMNmg5cND/+sA8Du24nUN\nqoesHmETt8ZfHT6yQ7jXQlC6aab0GUlLk5GsMA7O51sLXxoBkb5Y7vUmPV1i\n+SOWk/7JGBo4q0Jr0Xg4r0pPt8Dm3LdVhD4WigcXbAOJP00SeW2KMWMAmRW2\nlwRBmoGw1Djn01tu9oJQt+JqjjBanFAP33NSU5+H4e+aeYl+VoewFTwhlhmS\nTcblgVjHtl/p6Jm0Z3bEBrstrPdirctlXNNHL5EJp906Ly2PUSU4EYlfksWF\nMIpQ\r\n=eFw1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDGMKhIBl6YWzYpVxLKYHCDPMZjRwfinZjrze70FnVZhQIgOQdwTIjjBWe2duIqqxATBI080JfaltjSyyl6uyBVtNg="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.3_1577756574279_0.44375643342735627"},"_hasShrinkwrap":false},"1.0.0-rc0.4":{"name":"plumier","version":"1.0.0-rc0.4","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.4+ecc1f26","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.4+ecc1f26","@plumier/mongoose":"1.0.0-rc0.4+ecc1f26","@plumier/multipart":"1.0.0-rc0.4+ecc1f26","@plumier/serve-static":"1.0.0-rc0.4+ecc1f26","@plumier/social-login":"1.0.0-rc0.4+ecc1f26","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ecc1f263e66b0ba9304f68f9ca0d86bebbe35d39","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.4","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-NjAftcvBDhxJb5zKDErYrNAoAWCcPw3cC481+xCj60VIJ7LV4TzoFYJA6+PooSpv8ae7FKfLCA+B5QwRDjhvpA==","shasum":"ab72d71680ae4449811f628d49c46ad37c34076e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.4.tgz","fileCount":9,"unpackedSize":20896,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeDDmKCRA9TVsSAnZWagAA1lsP/jofrVoJuzgcjQjTS3Xj\nYkbjlSsWVbVLFc4ehIOhgJ9JWsnLnmSe9If19gvx+eGRKwMZ8eDRaFMAEbSC\naQaYqXgUzaeEoeiR/hHlW/MHI7ASazWUDo3kWuWYeXgxZhKYkyshUlkgz7uY\nh/dkF0Alhd84jeOHnVXw3IOPH0VV8p5HZkqxNT+iILbnGl7X9qXHV9IQLXMl\nttST0WELEZ6fGpQpSwgX6mGQA6yGt2SDjKz8DH/CcPD6Mw1vpCvw1H9tpi+q\n5A/R6X+WVDZrVbEcKK0s5uL5jCBPi2jXHhjZvsO1MpgwJIQCf9hTABeSkjOD\nNXLH1UJX6q1NO4m8CtTc57HxWJIIdpvByrDdbovlP45AeuG5u+0gE8IFVYvu\nD+ZW36AUSHW1cTxr1/AMO315qVFP68c3MTLxa6XlG5BAbQW/UDaaKKDKkkvP\n+XMNVx4++/nwfVltyXCZvCi0HKoNi0LWAE/wxwl2alK+CyUyG83u8yL9iRQC\nZazPjHtJUsyi+OAmFXwmyuVtcU2IY93RMI9dnrU4P6DKvGVo34bPxxOGrbHO\nG4VHU56AlWG0/9hISxuLw4DautjE2tpNa7gz+IsqIkUGpt8KNoCiaH8JDMpG\nZdq2x8LmPGIUo3GGtEvgOVhEktOdSJ7KQO+3Z0Pryg1nRQYpE6lBTzSCNuP8\nIJIG\r\n=Ft/L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxz/Fx49uQtICbAjhulDu4AyR2JyZwscazi/0JvNhjSwIgSXWBByDwS1+uSb7moTv5hz3kPVHkHl3T8OtZXasrEhE="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.4_1577859466323_0.1754903434967634"},"_hasShrinkwrap":false},"1.0.0-rc0.5":{"name":"plumier","version":"1.0.0-rc0.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.5+cf4f02a","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.5+cf4f02a","@plumier/mongoose":"1.0.0-rc0.5+cf4f02a","@plumier/multipart":"1.0.0-rc0.5+cf4f02a","@plumier/serve-static":"1.0.0-rc0.5+cf4f02a","@plumier/social-login":"1.0.0-rc0.5+cf4f02a","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cf4f02acf93745cd3fd1b65f7fc08c34ea83e0f7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.5","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.19.0/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-Yz/sN3/bhGaq8Aukd6CCmG0LBzffdaeMZ2OH12tGTo6+Mo0jfWsjIvIi8tx6IXK8t/r9hyEwJ5pGGAxzbHx8Bw==","shasum":"ada454964bf2ad0d0a8fec876f09c84e0e7daafd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.5.tgz","fileCount":9,"unpackedSize":20896,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeDUspCRA9TVsSAnZWagAAfjQP/RHhYekf40pYaC6y3BQT\nDsIARlxWWMtEnvDZmnrKpm/gOEZEDrw7uSThOSzzlGFPYCrurY6gxwb3xgkT\nritDM9OstXkyD40ubTvZH8k5NNNzdC25rXBL1tiR07SsPyjKa21y9OkhMOYP\nrIbFgiyouZI0BATof4A7W4DkvJQJM/s7nQHm8cxZblfhXTFCWN/hSn1huQEp\nr/M/6SaMKANkXj0m90rZ/80PWA4RUEYG/Esz6dbvZCrRkdyeMx0zZbtbrW6R\nOEsFIG4bIKE+HaTkE+I+HOI1Bfdo2S6OTm73RPM++IhXKGZuVccP/V9cIJfF\no4C2BdMEpuf3xfbhWCbORPPQzqJF995OenGRvV2aNpj+5q6Lnj/o8gaqogR7\nuJ18o9m1T0SF4IhQTeC9SFXJbwEfHOqFyncBpFPb2BCUjyTFHvvfIZMvznrc\noUJaUFy9YF28dqIg2o6a6kV9zyBxgPksLhUoxOB4hd+UuL8zUc9eMbQHPWXa\nErTnkBntcJCXmh0/PvcpxkphPCHzov31UXbd3ofMrQMN8T+6stlPAqEVWgik\n89kQAdUYaasUbfp03xo/AAaKm4crQH2UJ4IZDmguSI215GvkN+8YQ2xVJgTJ\nY5wdIGd1wcsp/dj8/knQhOowmcRclbTVDDArJtr/NTgXvlmT/4FmESf7b7EH\nlFdg\r\n=8goQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAEdyJrcAa0I1JJgAs54MVHgqyTKXQ5hdXWmzZa8tYc8AiEA/R5e1J7bucSa/qr4otT/Ec7mxKuNZ9ctOIQXRD20NdM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.5_1577929513191_0.7792671368733202"},"_hasShrinkwrap":false},"1.0.0-rc0.6":{"name":"plumier","version":"1.0.0-rc0.6","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.6+188fd19","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.6+188fd19","@plumier/mongoose":"1.0.0-rc0.6+188fd19","@plumier/multipart":"1.0.0-rc0.6+188fd19","@plumier/serve-static":"1.0.0-rc0.6+188fd19","@plumier/social-login":"1.0.0-rc0.6+188fd19","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"188fd191e39036aded6125a67aff5443d8de813d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.6","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.20.1/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-oY+9crf5ltJud4NuVQT3iDn6CLrSyRPjHQ7u8WpzL1CGFvspqm2noD1RA7Hg5UIBtbneP5/7Ywhegujf7aaRZw==","shasum":"399812e93de03326f10fcdd9a02364da90b5206f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.6.tgz","fileCount":9,"unpackedSize":20896,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeDU3vCRA9TVsSAnZWagAARNQP/1Vlv2qukRL5TOisE2ZP\ntAiF5N2pAcd8g4DicL4cWKGgo71HKRQDzaWhSfy8dpMeOv1s+qqq0N60kOyH\nBttTSacIPaMOYaOLEcgNfjb3jvqPe80tkQNY3wEWKlQ6yY94Yu21uMOWmP05\n+8C4XPYji13XMaqtZT2eQiokSVOYyPUxX3/z+i7+DPqXajJ+IIJXyMSbKay+\nwl/DhNQaKi594GIQptOPGKD06fwTZMg5DssLUffBvRcfF1BSuHdki94TxYwJ\nC5FnmptDTAWQvaUvfN+xQ8G/CNY9fL7HZWGieXSEduoZcuVvi3RQUOY2AAKk\nUXrFwyZhp9nROU9PBISRXw5iwma5TzQBBqAefxhYIky8wtxS6XBaYjEg68no\n+Cw81Gst4fObLxvVi+SVrpXt29siFNIFCZE1sA9CkU+CPR9B/dfQbUL3sD3N\nf9WDv7nBC6GR/Mc+Nzo2zNm0o7O63mK9nSbV4NwJdXBaNhwLS+bL4g04tXEC\nYwJoEemRxBAaoAU1Xn92yjiFeeSj2qlcWIJcbWdLAJbKXpcMeu7wfH7nIMd3\nwT0pF22mE7bna0ZB1qZg6OJN6DzZXMcrRS96vSrkha6/P/ePC7En5bCM5Rr4\ntDShtMj1AW9ZePz2/9zbN/KsniKRZpDwUi2CKOL7yfq3vPu9GWje7iyUjwPr\n2rRN\r\n=LwfB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmMTRDgEN76Teaxq4mumpWrT/vchM+0Ew3iCTnCJ61RwIgT9/jZksMBkzc0SFaMeQh1LCYhKVw5PIkIvnfdLu/WhM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.6_1577930223120_0.7729943796419954"},"_hasShrinkwrap":false},"1.0.0-rc0.7":{"name":"plumier","version":"1.0.0-rc0.7","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.7+4116c9b","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.7+4116c9b","@plumier/mongoose":"1.0.0-rc0.7+4116c9b","@plumier/multipart":"1.0.0-rc0.7+4116c9b","@plumier/serve-static":"1.0.0-rc0.7+4116c9b","@plumier/social-login":"1.0.0-rc0.7+4116c9b","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4116c9b262fb8e1ef798d73659c66b2404122f68","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n\n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nPlumier primarily created for full stack developer who spend more time working on the UI side and focus on creating a good user experience. Plumier comes with some built-in production-ready features that make creating secure JSON Api fun and easy.\n\n### Lightweight\nPlumier relatively has small code base which make it light and fast. It uses Koa as its core http handler which is quite fast, below is comparison result of Koa, Plumier and Express.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nplumier      koa          GET         33624.00     -0.06\nkoa                       GET         33602.19      0.00\nexpress                   GET         17688.37      0.00\nnest         express      GET         16932.91      4.27\nloopback     express      GET          5174.61     70.75\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12218.37      0.00\nplumier      koa          POST        11196.55      8.36\nexpress                   POST         9543.46      0.00\nnest         express      POST         6814.64     28.59\nloopback     express      POST         3108.91     67.42\n```\n\nVersion 1.0.0-beta.9 successfully reduce the framework cost, its mean using Plumier is the same as using Koa + Koa Router + Joi stack with all of Plumier features. \n\nThe benchmark script can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\n### Flexible\nAlmost every part of framework is fully configurable and easy to override. For example plumier route generation system provided flexibility using convention and also configuration.\n\nPlumier traverse through the controller directories and generate routes based on directory name, controller name, method name and parameter names. This behavior make you easily separate your controllers based on version etc.\n\n```typescript\n// path: controller/api/v1/users-controller.ts\nexport class UsersController {\n\n    @route.put(\":id\")\n    modify(id:number, data:User){\n        //implementation\n    }\n}\n```\n\nAbove class generated into\n\n```\nPUT /api/v1/users/:id\n```\n\n* `api` is a directory\n* `v1` is a directory\n* `user` is a controller `UsersController`\n* `:id` is method parameter, the method name is ignored\n\nPlumier has a flexible decorator based routing configuration, it makes you easily create clean restful api routes and nested restful api with separate controller. \n\nCheck the [route cheat sheet](https://plumierjs.com/docs/refs/route) for detail information\n\n### Testable\nPlumier controller is a plain TypeScript class it doesn't need to inherit from any base class, thats make it easily instantiated outside the framework. \n\nPlumier provided powerful [parameter binding](https://plumierjs.com/docs/refs/parameter-binding) to bound specific value of request object into method's parameter which eliminate usage of Request stub. Controller returned object or promised object or throw `HttpStatusError` and translated into http response which eliminate usage of Response mock.\n\n```typescript\nexport class AuthController {\n    @route.post()\n    login(userName:string, password:string){\n        const user = await userDb.findByEmail(email)\n        if (user && await bcrypt.compare(password, user.password)) {\n            return { token: sign({ userId: user.id, role: user.role }, config.jwtSecret) }\n        }\n        else\n            throw new HttpStatusError(403, \"Invalid username or password\")\n    }\n}\n```\n\nController above uses [name binding](https://plumierjs.com/docs/refs/parameter-binding#name-binding), `userName` and `password` parameter will automatically bound with request body `{ \"userName\": \"abcd\", \"password\": \"12345\" }` or url encoded form `userName=abcd&password=12345`.\n\nTesting above controller is as simple as testing plain object:\n\n```typescript\nit(\"Should return signed token if login successfully\", async () => {\n    const controller = new AuthController()\n    const result = await controller.login(\"abcd\", \"12345\")\n    expect(result).toBe(<signed token>)\n})\n\nit(\"Should reject if provided invalid username or password\", async () => {\n    const controller = new AuthController()\n    expect(controller.login(\"abcd\", \"1234578\"))\n        .rejects.toEqual(new HttpStatusError(403, \"Invalid username or password\"))\n})\n```\n\n### Secure\nPlumier provided built-in [type converter](https://plumierjs.com/docs/refs/converters), [validator](https://plumierjs.com/docs/refs/validation), [token based authentication](https://plumierjs.com/docs/refs/authorization), [declarative authorization](https://plumierjs.com/docs/refs/authorization#role-authorization) and [parameter authorization](https://plumierjs.com/docs/refs/authorization#parameter-authorization) which make creating secure JSON API trivial.\n\n```typescript\n@domain()\nexport class User  {\n    constructor(\n        @val.email()\n        public email: string,\n        public displayName: string,\n        public birthDate: Date,\n        @authorize.role(\"Admin\")\n        public role: \"Admin\" | \"User\"\n    ) { }\n}\n```\n\nAbove is `User` domain that will be used as controller parameter type.  Its a plain TypeScript class using [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) decorated with some validation and parameter authorization. \n\nPlumier aware of TypeScript type annotation and will make sure user provided the correct data type, `@val.email()` will validate the email, `@authorize.role(\"Admin\")` will make sure only Admin can set the role field.\n\n```typescript\nexport class UsersController {\n    private readonly repo = new Repository<User>(\"User\")\n\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    all(offset: number, limit: number = 50) {\n        return this.repo.find(offset, limit)\n    }\n\n    @authorize.public()\n    @route.post(\"\")\n    save(data: User) {\n        return this.repo.add(data)\n    }\n}\n```\n\nAbove controller will generate routes below\n\n```\nPOST /users\nGET  /users?offset=0&limit=<optional>\n```\n\nEven if above controller implementation look so naive and vulnerable, but Plumier already done some security check before user input touching database. Get users route only accessible by Admin other user try accessing it will got 401 or 403 status. Save user is public so everyone can register to the service. \n\nPlumier done some data conversion and security check, example below is list of user input and their appropriate status returned.\n\n| User Input                                                                                                                    | Description                                      |\n| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                       | Valid, `birthDate` converted to `Date`           |\n| `{ \"birthDate\": \"1988-1-1\" }`                                                                                                 | Invalid, `email` and `displayName` is required   |\n| `{ \"email\": \"abc\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\" }`                                                      | Invalid email                                    |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"abc\" }`                                            | Invalid `birthDate`                              |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"hack\": \"lorem ipsum dolor sit amet\" }` | Valid, `hack` field removed                      |\n| `{ \"email\": \"john.doe@gmail.com\", \"displayName\": \"John Doe\", \"birthDate\": \"1988-1-1\", \"role\" : \"Admin\" }`                     | Setting `role` only valid if login user is Admin |\n\n### Friendly\nPlumier enhanced with static route analysis which will print friendly message if you misconfigure controller or forgot some decorator.\n\n![static analysis](https://plumierjs.com/docs/assets/static-analysis.png)\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.7","_nodeVersion":"12.14.0","_npmVersion":"lerna/3.20.1/node@v12.14.0+x64 (linux)","dist":{"integrity":"sha512-kK58Y0sXtGw6P+nEx+Pq/MsTkUPiISGM03djGSbfGUBibXX4nWOyB7hx+ZZ40FtHNCB4zDzPoQ1rcNxY9KYD/w==","shasum":"41176a7acdccc834218908a6a25dee85e1508fef","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.7.tgz","fileCount":9,"unpackedSize":20896,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeEU98CRA9TVsSAnZWagAAI0YQAKOrsmr7iTmvavGqXwUp\nEXLDMxkC/7I1ryCHaPB+RQ8Nw0mXGxz35sI2cTavqrF1gbCQKlTjb00YgAyv\n5Ily9HaU35u+lXxcvPc5h/+zkRb4ZXRgfS4l4NV2EPAgAm0CwkDWqbuc9LjQ\neBi4vVVQtXBZyFJpxSfegRKE5RXtmSr1umjhNYeeCDD2phk8jOf7bSTNfzK6\n9hozlY7R4SMFUda7sxe7WkKyv5labe0PLVCL1dZI6hXeX1r5dTPyFPhMYkNy\npmguavlW4MqySz2sFqj66DHwCXB5XyEhJTIAY3mimp2vRgoKq6yLLwB/7xuX\nw3Q6hBlmwSa2lJYcXHjI5RRE+1Fd/gzXpcTOgNLUSBWR8BespcJ8o2fFfoH4\njyB6kiHwsv55lnNwUu/TWsf45e/Dlr74R7Ht0dXTA8R+KMdsF74GZ42ETGE2\ndJJ57Lafr9TSxF3sHlr81OqqUscFuB7WubZwaohxAVAsa2S7v6yWaOhwFkx/\nW3+qBuTB7YAkgf4cofo0z59hejG38nxgF4K6aPFzjnkaAdwAIa7g20XP+sYi\nT+IzvV7lGV5RbWDj73f02N6h/o4XCnKmTrWDSoKyYSQz7QzXexC5jDU9Szwm\n3Vy1iz+5J5PBMQIH6YlCh1YipQcPXidgpaYSOo8fT4zmjz+8pCB/iRnnuijY\nZNua\r\n=jCjh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCvNv/UQLBaZxsq2aOK7Op+GYc4jW3YU5U5Gx871G8lNQIhAKRte9UZfoLhwsRmb980HX2UUesmzc9TEbwMIx+m4bXg"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.7_1578192764365_0.5310697139065372"},"_hasShrinkwrap":false},"1.0.0-rc0.8":{"name":"plumier","version":"1.0.0-rc0.8","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.8+b1db82a","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.8+b1db82a","@plumier/mongoose":"1.0.0-rc0.8+b1db82a","@plumier/multipart":"1.0.0-rc0.8+b1db82a","@plumier/serve-static":"1.0.0-rc0.8+b1db82a","@plumier/social-login":"1.0.0-rc0.8+b1db82a","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b1db82ae0444bfb437142e09b3ffe216470eb3f2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.8","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.1/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-bbNPQdoO6a2boivRRlvfxGoM8tf2WAS/d0xGXNB+KiZztPGZkOAxmlsQ+QdJJvpEU1m8SgdRqqjSUpSqBxOD0g==","shasum":"34e166ef1304caf713739749f905710b75940be2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.8.tgz","fileCount":9,"unpackedSize":21102,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeFY/DCRA9TVsSAnZWagAAZW0P+QBCLLwea/a/bm0TvO4c\nFaEqoNQSGVbqGtoYrI49+wKbJuhAICFakQaTa8jmy6ZGuZn6DKZg25Nkc7Y5\njTBXEriKRlg9LYP0JNAYqjUbQECizmWUb1c6Uge3zaqa8xy+qMIma73ykQki\nmPqW5YXyT3Rb4j8qevHqhq4PQinPtYz2ZU/QNtonsl2bsib+YUrG/gGJGgWX\nzhhM/w0LEoVT0UvRn77/iIzRGmm+9sVjKuqw0SnLS2EmeR/tskVX/5h9gkSs\n0mVKa6c50JaG+XM8f1AX4NUyNx8b9A8nqSBYAmqluu3LThzoYPZm+n5gwipV\nzmFreN5nixuYGPCtBIV6/yBrnUP+UwpQUYCD9P0sKMgsZZj85Ig0nPZB07is\nNoGujGHBEbqxZHXwBwpONtSdOvDcs6PofzPMMueI0oSzwkDVHtGWMTUIGZDP\ngo6HxEE0EUpaNQz52Jc6MrWVr+xil0QS7JRDKSmNCKJNP6ouy71Toxb75qRv\ne8faSobH/rJGa+dJCSF6pfPdsnenjw+bjiaaFik/tFOXOpqTDrRrgVFLPHCv\nu8ymx0rErk8iRM1GXuIaqilCF76sLF9RgQ88+Ddg1s0NoUvTP4eAftjo+46G\nNzDKkXDBP2QN9nSj4EmivWMKbXeJDkWf2DzIU0Rz6pNbbaqw/j5+B2W2qUnY\nJrLM\r\n=cB9i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHoGmc4r3JgYzCXqWCenOoblRBrdmLQvzmAFiLzfnex0AiEAtB1QDErd2l19ACXrJdpY0KceoZYwNgcxEcLY28FcOFs="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.8_1578471363611_0.9599082260957297"},"_hasShrinkwrap":false},"1.0.0-rc0.9":{"name":"plumier","version":"1.0.0-rc0.9","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.9+d0ef09c","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.9+d0ef09c","@plumier/mongoose":"1.0.0-rc0.9+d0ef09c","@plumier/multipart":"1.0.0-rc0.9+d0ef09c","@plumier/serve-static":"1.0.0-rc0.9+d0ef09c","@plumier/social-login":"1.0.0-rc0.9+d0ef09c","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d0ef09ca843eb7391d0d16ab017fb154640f21ed","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 8.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Nodejs 8+\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.9","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.1/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-SYWLTy5GwQebQ3Qtkg+oIcNg/G+24q+TexDoa+Ejm6UVWm+qTR91MEFfG2NBjKD+s/kMwYTsc/XEVQaGBqwa2w==","shasum":"a363227b97231f6b7a431660c718932bfc2254b6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.9.tgz","fileCount":9,"unpackedSize":21102,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeFqC5CRA9TVsSAnZWagAAr3kP+QEySbdlnC923UjXI2lM\nSyjLGY+ERL74cYDsTWEipMj4otFXtqFyLofzogemVhIVH1VLhHZgX3exjEtQ\n0XkDeCERHRYeX3KGyp/Z1gFcNMqCwo+4OQXHOoVHEjIYGfe/ZFxLPnRMfxJ5\nBGOwuFaRnTLCeCDhjtGUnBVcWwslJ6hJrQj5ezjnTcNBm+asNIMglUK05hO2\nhpMrY0AXhkyGyDbmijIzmU/qbP30hE/iHV1n6bEvrCuzbXFTy8kXy8ng/VKv\n9DreRx38M7Gvn+IFUvMD2I9koZtnYE8Vw0n/7F+DvsJ/+IOInUvgg7mW5oHR\nKjL0yVPubTSniJvVVfo94R/CurJ3DCOBSwNJSePcSWqFjef4XXnzWHZ/ERXZ\ndqv/JM6oCHWZLgxoZ1+d/+fo/Y0DUMcpE7cmvBXZh5SwAiMvfX12td3rkOI5\ngJ6XX2jSs8rLrbaeXH0kgIGYqnuPNBxObGdDSS2XKYW7cOY2hTDtpbWjLi8e\nRmxxMErRsojhLpaOiSj/DVRDlROYojlSvNbschHUXfJNDzM8ugNegcnr8sXe\ndZVjtombvxcOjy9AlFgL9vkQVCm9euVgSNskFNCQkqZBrBGSiw1m5fah2J6i\ntic974gDq1zkSgWt5EQ2/ip1cFAZhhmDio0+ncARZNIkuX0/u21b+xSjT5sk\nbD7c\r\n=tazB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbrCN3fqSgBlRstzm9mejGT+2plTmRe/rN05HFl0BtPgIgRM4uPOxWai/RvUSgAsorZ4kEsDeNliUdyyZYwGK1LqU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.9_1578541240724_0.2795254154647655"},"_hasShrinkwrap":false},"1.0.0-rc0.10":{"name":"plumier","version":"1.0.0-rc0.10","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.10+02cb664","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.10+02cb664","@plumier/mongoose":"1.0.0-rc0.10+02cb664","@plumier/multipart":"1.0.0-rc0.10+02cb664","@plumier/serve-static":"1.0.0-rc0.10+02cb664","@plumier/social-login":"1.0.0-rc0.10+02cb664","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"02cb66433e32e10babe7e891d4c9588bfc4932c8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.10","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.1/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-3JxhhuCASCkQSLjl5w/232we3VMz7zeIc7Up1zzI8YVTXEYzKHXZ6W/XWd/uOeGJ2s2aQ4uOhQwjtoBRIoCXZA==","shasum":"aadb7ab678b94612f063ff003fe73b3ed080ff31","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.10.tgz","fileCount":9,"unpackedSize":21098,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeGtpjCRA9TVsSAnZWagAAWe4P/3pYo/VAVwKRmcfDiwGi\nwUpFWpwrw3HmjLEG7nJaD+dkxvByOt9On/vUZ8oCNqkf6VIPbrAgQWNa93jN\nvzb7NktCqziThcOMvql2YvTbN0HHXcaB21nUuDkT0O0gSHwf4E3I25AKGgh6\nCz6K+nR/swzV5aA3NBuVRJSB+9VVkMnOnskm9oqXC1cfOPqfpDx70mR24zUr\nIlXvxVW0XnlavL1+9YxHylo+i2kK9BfrA10n6NMCpMtPR/Oh+a5pt1pQ513w\nb1akVGGXuvaiZxZdADR+3yVxBwrIkJZZ1sHH5NEadOaKr8UnS1ZNlO/NxUwo\nBaeS7qHGTf5cbYS3F3g98P9HGWgBsF42HEuq3qLQPHcNbLbHvo+gkLiexgPo\nhkJx+j0feApiurg6iPm0xCQDPMRMWcL1RBnS1MilJ01DccXGRXM2/KJQszEw\n5EAXduIblIgMBqNFN55K8B7xkkvfFky4txMwgqsN/zbMIjPFTNJlg2vPSpOS\nNtGNTVZcs+yhC2TH5/93QrztPgzq2BqbuB+8ozYe6FW3DCNlD5hzqpnEzD59\nP90tnD7rETv772xr2zSL6QNA/4SXfLos0xpUSCYPF4McCFzGqsTbz8XVaJ3S\nflvQl8dssU2uWHSKa/X+2ET0uBknrvrY72r3DIFHhHV761mw3K7h4kWtAdtD\n1KPM\r\n=8H/G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDzRJzMMDVTk9Rw/VCMau2svDq81JG4c1Rq9n9AYYyqNAIhALQ7tXvmd+cMfedEd6VGlazZ3YN5ZSnkoiQ3VoeJgo2j"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.10_1578818146921_0.31496459522995623"},"_hasShrinkwrap":false},"1.0.0-rc0.11":{"name":"plumier","version":"1.0.0-rc0.11","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.11+a880ad9","@types/faker":"^4.1.8","@types/koa__cors":"^2.2.3","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.11+a880ad9","@plumier/mongoose":"1.0.0-rc0.11+a880ad9","@plumier/multipart":"1.0.0-rc0.11+a880ad9","@plumier/serve-static":"1.0.0-rc0.11+a880ad9","@plumier/social-login":"1.0.0-rc0.11+a880ad9","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.5","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.0.42","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.1","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.5.4","typescript":"^3.7.2"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a880ad980248ff506d7640572b05327b1286fbf6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.11","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.1/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-d1xwWO7L5K0avmkGbUQELKq+lzhc7BlLTV5V1pvyfVoOZKGRE63YT6eCbB9AmF696LIybN0pDqMBBnCxTUImzA==","shasum":"4f0f1d77abd108bfd38407e1cd587a74d8d34353","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.11.tgz","fileCount":9,"unpackedSize":21098,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeGt1PCRA9TVsSAnZWagAAE5UQAIITZJpsbHbv5ySS/VBR\nQwD2UL1QJpSPsPmoevSm3AFevyhGjj+QYdmQaANnIr9QhlOH4dW7tfxE4K0i\ndEtmur4bKDjTwUkuwou6s1ZzgsyMdmx/6K6/svYqN2VyrRul6cOz4lL23L1R\nOmhhNjaGQr04r4G2MUWuhqlYDKmoCI3DGX7o+fUuohLVOMQUcrXOu1cnsiVn\nrW6DfmDQAl1ayYZEbkel1KK63aTgmltNor10F0kODfi5SShUu22ZHILrjUAF\nTfwSfx+XH5cnDmFgOAEKKH94iXhFRHRZSjqccutbxy8cwX2DlY8YsTW6/szc\nV3aXe0GsDrJIjvo70rUTR+5Dw3jdEfLA8nIKke+1YjBYBC7pICbWXKmYQb+V\nhSCGyrByWazMDJih1T63R7yMS3kZJE1Xhw0cK37Hb8vbBytcr/579crgedXI\nnzbE/55upoAUappTux7pZHhoLI4Va++Y0XtJOQJXofPlUt96ZeGueb0inLjr\nx/0tPX7VQMy5gH5WiV0e1QbkrS/iaOzBSNXGrT0guVkyzTa/qwmMIn6/lw0p\nkGczv+p88kMxO4zmJw/oSDQ62Mz+stOoVKH+kVNKeh6p/nvSQROPTL6c4MUa\ne0MEyO4wjCVywsd+JAU4t1wlxZRYlxHu2/90xNoAw1t+Znww5QeHVg20e/mS\n07Es\r\n=IBFg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEckO9icbiv99Fyfxy3r0X9k3+94JefBzv3WA1uc2vnsAiAsXDXHfmWGhyfBuYl6i4FKYFShrUe57AbNX639+bCcOA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.11_1578818895582_0.27219581952084204"},"_hasShrinkwrap":false},"1.0.0-rc0.12":{"name":"plumier","version":"1.0.0-rc0.12","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.12+9669fd9","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.12+9669fd9","@plumier/mongoose":"1.0.0-rc0.12+9669fd9","@plumier/multipart":"1.0.0-rc0.12+9669fd9","@plumier/serve-static":"1.0.0-rc0.12+9669fd9","@plumier/social-login":"1.0.0-rc0.12+9669fd9","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9669fd951e20f47a2c96a9bab360e92d39be17f5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.12","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-GRl2a7YcnsNji/pH9UBpzQutGBmJkP+L5vIMLovmnbKfq38sLFUrdebrkgUvW6yG1R8Uydfjy3J+DmXIxXxfdA==","shasum":"2181d123805263bc9e5a9c829b235720831cdfce","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.12.tgz","fileCount":9,"unpackedSize":21097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeHizjCRA9TVsSAnZWagAAXrgP/13n3pjea1T04d1f2UdR\nZggxOiQg/GZlBZeO1xj0rjuil+xV66bSzq/SaYzgpHgiIOhc3QW+iAc2eFPq\nnUkQCxUx/aTrkVIl9TkUB6gNuGPtrav1yuVaTUJGyOQK3GNyjXiWTE/+O8aC\nlBnGG8O2e8b4lx3Y22O5iRNZktQQqdzd/p889ZuPzex5VaxrRp7M9Z3mGHHG\nAo8eHIli0rgQkJfyigGKwlWXfEOF3xgPdgo9ROeCFbdyqYWBtPzXKV/N+AW0\naXihue4q2rIT/CEhPHUfpk00MLQJODcRuLH6+yFoQtt4bVJ6LtklGSMY9bWa\nnWa6O6sIpSLsvuuAZPsrsyY8xBZ2e4V6epyRb2cpXkwHxRd0cYYaxVFG4RcE\nhEY4jrUZ+o6zzyO2EDtls/7A7ZDkPLTag5Bdj0kzex98kfGtmY/qt1O7k2eK\nvqEIfMWjoi2TpMRzEAdNPBdcpjew8Fc6uS6Ww6+7q/Y+1c5HQrlvy2jUo0RW\nap5OoZwYmkBGZaao/9Lg1IYGbrTonoMZ/uqMZb28L9A0b1UzHwjYZJDXC7ig\n0y5Fsigslrgy9At2r1x7Cy/JuGaB5L738g/E7quIC5uWXg1McYdPFa3dsK8l\n18wa8d4nLproH3hxZNyIGJUUGCRJ+cAc6WESCMYYJieWF55HDP4GLxPXk04r\n7Wat\r\n=8TUn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHE3IVIed5TsxJ7IqBYVBE+yUtYy9kSDtXw53F89vsexAiBVdKzNollRGMgUUgbosYij2WMPBum3hlK1BvH9S4FYVw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.12_1579035873952_0.5321605184777365"},"_hasShrinkwrap":false},"1.0.0-rc0.13":{"name":"plumier","version":"1.0.0-rc0.13","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.13+60f0872","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.13+60f0872","@plumier/mongoose":"1.0.0-rc0.13+60f0872","@plumier/multipart":"1.0.0-rc0.13+60f0872","@plumier/serve-static":"1.0.0-rc0.13+60f0872","@plumier/social-login":"1.0.0-rc0.13+60f0872","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"60f08727cb458a9d38ab62f943d1bb1f1df2a2c1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.13","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-cDFBGJfQL4hOb9qJn+3oFDRJE6NcNJCGwQrayc+nNDl3W7XF0UrlA5c29K5GT40gOsKvkOE1oGt9eiiDRGzXvA==","shasum":"004d942de3b6a32edea04241912880437124b07b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.13.tgz","fileCount":9,"unpackedSize":21097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeH5lGCRA9TVsSAnZWagAAYJcP/Aoy2Lnaym7nOQFUryrG\npMsCJdOyrqRjTDLjxxeeKPHmM89iUr6RAK/fdGP/olgqrK9JfyT1TQ+r10iD\ntTla7SAh/HlQsStavIfmLxtLmiNK4nigrGcVkbDGBi8eQbtCneG2gK4PIgj9\nGkv5TjPYJOPC1RvOIOnt3KFOXya6WJPPDaDAeDXs2HmeX3XjWlT/KyNNWZ6X\n82PknE8de/VXaUFBFKuXmqsw1NfKmVkokPrUYfZGi0kM1wSK0KIffvrZYDKU\n+CNKpYELHFTXajWAayW3XNC+yC4s2f2XVVYAHd2BKYDDc3JpsVIix7EDxOdT\nEaoAU4HAk/9Q3WtxH3908O8oq80AlQTYr30gpSeZUYeFqz3uPQy7thPSKT22\nKbNQ2aDLmIqCNl/aUtebFrR8B6vJBdM6jWSDl/IEOInYzRWyU9+pdIeG4Kh+\nW5fx0jL5MTHqM6r0n5GIJkg3TecQOiU4iKAB/J1mtfx7dw5Z5v3SJngsgKiW\ncT4vo3+VGI3vCd3QMlWpaKA0ogZG1Q/tlyHPrXkMxC5n3Kj5Y0VGZQb5wm2k\npui38J3qy/+NI7WlilHqZENXGLQ4Q9yWgxSKknZvVKEReR0SAeEow4kYx03a\nSIJ6XCZN/uxWeunfs3xYH0clj78mAO/frfRloi4agJ3T7O6x1oeD9TIA/QOI\nEVha\r\n=haMl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICpgXayKmJ1Eb8ipF8u3cddeCFBV7iSkO6Fms8q+mtWyAiEA86zTU5r39mgEzV4gMCvyadFzCARBrWUHgjnaSal8SF8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.13_1579129158383_0.17071935784397274"},"_hasShrinkwrap":false},"1.0.0-rc0.14":{"name":"plumier","version":"1.0.0-rc0.14","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.14+d7d59f3","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.14+d7d59f3","@plumier/mongoose":"1.0.0-rc0.14+d7d59f3","@plumier/multipart":"1.0.0-rc0.14+d7d59f3","@plumier/serve-static":"1.0.0-rc0.14+d7d59f3","@plumier/social-login":"1.0.0-rc0.14+d7d59f3","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d7d59f394658c8cafb60565e547979ca0b48b4b4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.14","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-6src1eLetK06fKkML8Bf/un1cAZgAnT8hzF1YpERuqYlTshfXfdfoYSFuQy8N/sBWGgP/H4DUd5ZjdLTr3bwaw==","shasum":"eab5172f3f82c3e9c3bafad74ac19bfb5e9b8bee","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.14.tgz","fileCount":9,"unpackedSize":21097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeH6DXCRA9TVsSAnZWagAA+X0P/jleXftcYz73lCUZY2Fi\nPNi1HMdJLFS1q29fBQjGI9vF2oME1Y0Qbr2Jv+eVLusMwMtq6tQJmpL2szGM\nN0ispTmg8tsxiyLE/lCGsjm/HCMiukjXaFJM4AB4p+v1YloKt/liMAMexSBr\nI+QyqOwnvso+5NAMye4kD3QWc3dbU6TtL7Uru5g1L/xbGK45Ulb4VkFfzDA0\ntyGuSvFe2rq2EkKBqbFgFEcfGDR6qmh1iud3cbYh59lmlNop5qCJ0Pz+Uwm0\nImGRvtMu/8Uxw0gKXLaapvB5cL1DN5W65fAq2A/g3lIMwO46aEnH1S5esmXn\nrq8FXFCwGAoyU6pZP+XcF78U/Nm/v6ien9JbHrwxQPpZRWNZlkIJEAaP51o4\nd8hk2RP2O72lJYTemF5BQXHOT/tKBYoSWUtlAw+nDtDomL3Uj8BFcy2MId1K\ncBCXoxifqEFSGFmgo4//g8M9D4xD9dQXP49KQJ2GLj7vrH4O4NNA9FaIuDxq\ncUjtmFKpNEj18XahXwTWAQfaliPlb/K09MR6y+o1i8c163bmt8WmNh+eMuS7\n7+JtPYkW1GTCe3w7RozfncblLm6IxPEPRpWR+25m78IIWGHd4XlB0tQCokBN\np1AQvDlKUZCH5436nqqP741xgSsvF6RhY8ExhgoaOE99BvAvZYVQqt3L+n58\nYt4U\r\n=8yNV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGz2tkt9eEotnxfpbnlwz8OPDmUR2pPnKMSU3Zt4ctURAiEAsg0b+yJLaG3+xrYloR2Q16Z3dH7dmnkp6rvMWkmwGeQ="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.14_1579131094733_0.5286500031606476"},"_hasShrinkwrap":false},"1.0.0-rc0.15":{"name":"plumier","version":"1.0.0-rc0.15","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.15+dcc3702","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.15+dcc3702","@plumier/mongoose":"1.0.0-rc0.15+dcc3702","@plumier/multipart":"1.0.0-rc0.15+dcc3702","@plumier/serve-static":"1.0.0-rc0.15+dcc3702","@plumier/social-login":"1.0.0-rc0.15+dcc3702","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"dcc370216d6db9a1a0d2fc5e79052fc70b6200c9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.15","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-OG3KD2bqVXIyT70EKe6+vnEUXmwnepSmPTklsLcc1BlJzEoSIzNgWbiAm1B5ZD0Zte1qA75lY7gsYhyHN4jzcA==","shasum":"73d551d37f2d58b35133bcb637e1f91207cf239c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.15.tgz","fileCount":9,"unpackedSize":21097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeIjFGCRA9TVsSAnZWagAA5OoP/AnrVw6XaU6VXwn9bbkc\n5+pCEqoA2ltiNMwQXF6PZjj7JqX9lTBE1Y+P00WVzF6NgWsrP9YXbOcUX2Qm\nDxMh19swe4+4r2qlBe6c+sR9oUppCDotE1+PZ9f/Z5vtgldvWgV89XcYBsQg\n5toab84h4Prs7sTLPePZv3bw3z40BVc2vuaUzmrjri70KFP7f7CRckn4R1b6\nfmJDzZ0SyDzYVCBU2SLmrcAX1PN8T+fZ2Bm8VztIL/XRdTevUzBM63jEBbGa\n1dKXfD8Ggf0K5l1tOy0W0b+aTxltp2285tzqqF6+sebRCeCDdzrnBCFJ3rfR\nOg2r3VtzRK6+BKPyc1ir0/LjiyQDlfO6qeoTxCqQ1SAhu/hv55MNN1R1eH94\nny9mIsCALpUEVsQiabiHL8jCSxPUFY8+PKLDinkmV9mWqjhVzjEJXHN1XncT\nP4+/jjyviYoBU9eQu4Ik1ssldrJFKj/mrhS9CpqtN7pEMK2q8hIJUHWgSrWr\nrA17eAvncgFg4b1ihxtuL3MU+m//WTE5BwLPBeTfpMb5g+xaMRu76Nzmnlip\nJpEo3DT9EEsVTySHVVq0UvMXGsARNkocLn6xWjYp/ynkgFn4jhEHwEV+gwBL\n9I5hjDTKovxUo3SBDpluIpR7REZUMZicEPXwvQkr/MHRCk7WgJfkxqbGriRG\nv9JN\r\n=4ecX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAZl7BBgpiqX7CeC4bbchPkjcrCn9nHDW8aQIrcM4vnOAiBiCuxE3hThshRTI4Me/MdnhloLDf1xZ5bH3GbIUjc6Iw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.15_1579299142036_0.4762422291814681"},"_hasShrinkwrap":false},"1.0.0-rc0.16":{"name":"plumier","version":"1.0.0-rc0.16","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc0.16+969b61f","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc0.16+969b61f","@plumier/mongoose":"1.0.0-rc0.16+969b61f","@plumier/multipart":"1.0.0-rc0.16+969b61f","@plumier/serve-static":"1.0.0-rc0.16+969b61f","@plumier/social-login":"1.0.0-rc0.16+969b61f","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"969b61fd1d4691aaecb08bd3b7fb7b489e3606be","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc0.16","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-ehYNWKqYI8F3rgR5n6OfgF9t3vdphnzXFUPjCFFUBWAt1uXpB0p2Lxz1jTc3j9VcFdNoXdgJdWeDUOGlZaLMog==","shasum":"459cad507a6abc175f5d71ac8a9a6da5496e83bb","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc0.16.tgz","fileCount":9,"unpackedSize":21097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeKRDxCRA9TVsSAnZWagAAIFMQAKEbE8LzNitp26z8hrj4\nlLRSJEUKdZeDEEPgIKSUm8j6moX1BWiXjEnHFMxiir7Dt6FoQaRZExuFFzDI\nMjp44b1IUHStdWtb/GpljfXSdjviIQIZOg4XDtoyZ8fu0vuIVA30QZJb399M\nRY9jCJ+hEjT7qCpiqpLeHIYjlovmbP4YmrZz4Db9zlJe9+XhmKs7+E2CyOwD\nYhcNzD2AC2k/XNSmkm9bS1AHCfhH3QAw8u4/+ARmTw2qRSgauzA8Yk3cUEzA\n6/nB3LdW1AgWqZRhNSRtl2rjirdXlB4N5XezyFIAWBLDTGLlsvXMs0wDnh2y\ngDrcY4PQMU9xcKiQRnEVRgVX1d2mWJ22TQwNooYop5e3V4WzMhaHOdEIjGa0\nkrEly9IgEEyc3h6QvtXl23nj7D51g2jD2xME4Qj+myGd4l9gcgMXAtuNeYmt\nrZKX80X71JnxMbawoYEr2dDp6BQO/exsx+Eg00AuFfemV6QCvihNUT6+G11U\nmPnAXmaktgQ62Gp6COqX3KeUf72w0nJL/bhFOEhwwzXCW9PfqM+K2TEGyXHE\nKdMuRi9ssTnKIovcz+HFEzRYGX6zStWsqKg5jnY2zM/zi2FTKdjYULB9ryCX\nANHla1jFBYa3mvJzhfz5GP3oyoqd2LcmalDIjg7s35mpC4nINisedv1lolMR\ncOCa\r\n=jaKT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDOXNXhG0CN3HiqtZ0fdwAr6lRlOSkEpJXRrH2YjcMj9gIhAJvTKWCN5ugdbF4nTcXwbMpKgDxbBD4paUnX8vXk32g/"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc0.16_1579749617146_0.08176699115300279"},"_hasShrinkwrap":false},"1.0.0-rc.1":{"name":"plumier","version":"1.0.0-rc.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc.1","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc.1","@plumier/mongoose":"1.0.0-rc.1","@plumier/multipart":"1.0.0-rc.1","@plumier/serve-static":"1.0.0-rc.1","@plumier/social-login":"1.0.0-rc.1","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d301102d2b4bcec1533280ba7c15d37d8ff6e5e3","_id":"plumier@1.0.0-rc.1","_nodeVersion":"10.14.1","_npmVersion":"lerna/3.20.2/node@v10.14.1+x64 (darwin)","dist":{"integrity":"sha512-NGmHab5fzlgLKqZrep8r2Lm/stjLTUKSn+gcbmKFp4eRkXD5R8q2AWcZk2kUixaZKisw2CX/eib2Q7MBomvdkA==","shasum":"e3bc0b38989a8d7c32ce4e6af5586aeb0164421b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.1.tgz","fileCount":9,"unpackedSize":21027,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeLL1XCRA9TVsSAnZWagAAN+MP/35Ez8ob3qMovqrp9N2H\nPLEW3Spljej9On/W88ZvCdYfoH2Xa7T6g3SL4bhceOrbecICQMqhmkSyi8/7\nLB7RdEofx4nkp8BUlUBpko6PjI+qV/TWL9doIL99ebyYDnXcEfM2f1iBDAE9\nIE+DI6Lkv4ZFWw6ZsgxLHTWMhxRrOC+zJQIAEwZuTS5AWq/aF6dI2+IX+Zwx\nrpwTDWCvOxRM8LP33e2vpQdvkl1vjFhjxW2Cp86xHH2gw73XWPsz99qMMLPj\nTIvJmrJZxz5meHSAMHUMplmxTRqDePkp1yYr7FiLwZSG7f/1Hwoa26lmDDgk\nOA1SroxCQJMspSgC7JDVZtu8WrUze9Eb4mXWtYRcApBUWbXh4DtIcHV5xu2u\nS+LHhBOeVKMPxWs+aK2rkz9xpk34JHL4BMWkYDeSC+pwYgXRd+TGNK2URGnK\nuyV9L8Uti+HiFBQve05MHtCQnuRYKkRQMSLjzPhZ7HnXLTz730S+HO0IMqqu\nonyLuNj5F0EJNLQQP45O8piVzcMXLQbz2BFR6S3D679ymydKukOqfd0jql/F\ntWJgtfjvgUV60WXPn6+N6Z15ai38iahv1NEcLpwUmcdWTXy1YzXKBW0KcnaT\nmLoUtdDNbjTsAd91pMX6qgVZOBw2Qbq0r70jBEzGnGBPLtWuyT/GOvm+Y1HD\nuNa9\r\n=9PIx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCHzhJzbZLm9sPOrvQHjbp9Lhla61KawP8vdYZUOK98+MCIQDK5mRb8OQvMaeQoeSOL/0S3kHb5G2zfG3L0ep9ihbcwQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.1_1579990358673_0.1657787361910481"},"_hasShrinkwrap":false},"1.0.0-rc1.0":{"name":"plumier","version":"1.0.0-rc1.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.0+fc15acb","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc1.0+fc15acb","@plumier/mongoose":"1.0.0-rc1.0+fc15acb","@plumier/multipart":"1.0.0-rc1.0+fc15acb","@plumier/serve-static":"1.0.0-rc1.0+fc15acb","@plumier/social-login":"1.0.0-rc1.0+fc15acb","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fc15acb3bd45668839c831e79c6c124dced7bdc6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.0","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-Vo/EUBnfR4inamMcIaIomnI2aANDAScD/HXhVhUQnwC+mFMxSRjt9Q8+BGVM0mq1gFESfBZT1dLkjjvZ7OkdPw==","shasum":"8c8e91fe5467a1cf3f16e8839fdf8a6e70be5364","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.0.tgz","fileCount":9,"unpackedSize":21090,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeLMT7CRA9TVsSAnZWagAAGH8P/1butF6PTzOCx4e7rFFo\nSJ7ONOA/JkjesTh4vcrLlJuUgbFUme8ofdvmCps84Ci6OvEPCqQfyGWN6o+W\nKUB3pjAFoncS4LZ3W6CxHw1TOxBr3TIVQk/EW8Bh23JE5t5HG+eQaEps4CLf\n2i2zNWBG/e8zBLAK2vEOHMbnTqoo2VF3k2m8s6Xur7kzrj9TbuLpKNWLFr4u\nopTYRkuohH91gCGRLpHi5I0IzaPv1B8xXGjMPF4EWtVN0wT0cYmZo7LJl2Qk\n/BerYO6YktES48QjDsycCDh+ENfB+7O/RH1vlBl+5m3oxuD+Auod3uRXNCGL\nzZVc+OFNZyMCp/7Tr15T19HhqQSIXCRSX2/UaPavnFPs8ZDNEB8QRKL47ojA\n2aYnPWqPS0T7Dh/Aql1SjFnGWeoyRqQwyu+bbXrlKa6A+y4kJwpQkJnaq+dg\nss92QSlNKr+vh0JMTwrx2hhgjnLdjM4NwL3AiVuA3TVJgvwWfVCmrrHEByoj\n0ViDz77A9Y+6ZRe/D7Z2pPA1HXWh1XVD+uMjm5X73osoBisZpq0vMqBDwX3J\ndt+ZrU8iIg+wfianRVAW7TgWbvC+mxbnzka6NoYbY0RLIQbz683208um6qtG\nXIKrMhh1N6k2EseEnWVYOdAF4t01msvmfGc7LC203Un16Qcgg6qHRub30s1Q\noyBH\r\n=Z85Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCQdh9cUwAh640hreweg5gsN4HvUjY5lp6/lv5XckKE0wIhANcYdBxuWYUJXe4EQhv14yHTX0ekXbe/xM/umijm2NVZ"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.0_1579992314728_0.7908898787589203"},"_hasShrinkwrap":false},"1.0.0-rc1.1":{"name":"plumier","version":"1.0.0-rc1.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.1+7461f63","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc1.1+7461f63","@plumier/mongoose":"1.0.0-rc1.1+7461f63","@plumier/multipart":"1.0.0-rc1.1+7461f63","@plumier/serve-static":"1.0.0-rc1.1+7461f63","@plumier/social-login":"1.0.0-rc1.1+7461f63","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7461f63358a43d003035ddfee01cfd73172cc756","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.1","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-7MqqxdumeOSdCDvk/3BTd5iwr9jz7l6IeRBW0TFd5sdxXKkmN80h4Mo8bCsq91lMKSINaT6cqDc9F9eJqjTy7w==","shasum":"941e3ab7452dac002d055e8c84a34396a8cced0d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.1.tgz","fileCount":9,"unpackedSize":21090,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeLjkcCRA9TVsSAnZWagAA4WUP/jgdX/56R/HQlYrPcqV5\nCpILTK7od8osiZsZSarPESCT2cpoRaDr/cP5CLQXXPLKLhpDypydRmObRkHI\n7+/nMhcy7Snx10l24dnZRpS3AKKjPj9N6KrMpMawpiRP+jVdKOiWJDth+giB\n6zk2CNXnt/mnPe3PvL6qlYafLpy3YNY/Q24JeNmhiQUZIpGT8T1JyHk3Zt1H\nDzEi1Y5FNtJNq6n8gz4a+AwlmwEAFG/cKK5AYVVh4+9w4JhCIdry5Qz/OZ/n\nUeKZ9zwdZQ/j4zvStGh0+hjmJUaZ1yzKa7Sev/K3D56s1McmnmzWp+kguzYg\noUXxlBf1HWIwxZtsuIZru+V3BjrY5OLPx2+CgjBe91TRVO+ZSdpP7GkP/MOL\nUwiEl5W58cg+84RnJ/vK/nkcMjgo3dDUcH6Uvjt26PLP4k6k9kFC90uzAbd7\n0qjfW8RxTjQJWENOv4pdQ2ZfqDc5WZAtsWLcyCnBjywN0GGzfk6at0nJv48P\nLvKnOo5ZAX7EsxYycn94jV44NCBe5c8bfQyixd/NvNm5dfRaYr/xyEyUL4zw\nXlcwo3wnUQOOD5ZXBrTNRMNMP4PtYm0bGKWzClKc86IK0Hpu6Bwrha6hb97O\nFIpaYGCOXtmAVWIXN6hHwDQmio6T25sC+2Qx6WPGIVZIATAHCOxisTjEFSJN\nzU6S\r\n=fz/Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCa7lwFd7a7UwZ6FbA8bxIC9bvgtG4k+al8ArWqQYhzRgIhAIye6oBBpwZCMNxlJMvrpCsZ6dZ7Emq+mDsEeLHn2Bar"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.1_1580087580471_0.41007963767106226"},"_hasShrinkwrap":false},"1.0.0-rc1.2":{"name":"plumier","version":"1.0.0-rc1.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.2+28d2054","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@plumier/jwt":"1.0.0-rc1.2+28d2054","@plumier/mongoose":"1.0.0-rc1.2+28d2054","@plumier/multipart":"1.0.0-rc1.2+28d2054","@plumier/serve-static":"1.0.0-rc1.2+28d2054","@plumier/social-login":"1.0.0-rc1.2+28d2054","@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"28d20548a24f1d4990112ad33e4cadce746a4ef5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.2","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-eIEEqiXAUI5iw4fQQih24HzCwyA7ztDP+C0cPw9d3yt3IFsBT+yHRaDPgh+XOK9oEi7fLs0x41t2zLQ0e2MaCA==","shasum":"0dcde9558957a466cd4e049a387168065c5bc5c7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.2.tgz","fileCount":9,"unpackedSize":21090,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeMMaACRA9TVsSAnZWagAAIw8P/jtpIKCwmuFvL/WS0aod\nwOd9jUQuprhL/03RNn7QShq2XWwFemguJ3Krw0jqAdJqyfS6NSorgWJbwGRY\nA4BHB3NW2+G8T79w1Lj8xzTcMM7IauL7nVWXBnmHpVBKBWuyh4ybIfwytszO\nW1xYswiHdK7TzxteY9pKOdX4Mk8AXjfD32dlm+5WmOckdVL3cY38oHRKNl8L\nc8owlZIiXecIfmZoje1SSXXSsCeKD7hdrL7YnETiRsAaYZvuI3/ngukB3vr1\naAq24cJkCYp0ZKwAhQgJ3OGBN4XD09kgx88m7Jw2BYNzMYRhIHjb/KZQeb+O\ngXZlwqo6SAvzbuS4M56NMK123kg/gGZP2K4lUsPC8XleQm/8wj5iwrfiqC8W\nJOEEOQnj2ANMKdkDEbGm1fDAwC+6Z18IS5T13Qk03KJSXGTVVws6IS2mO3Nq\nv8fdq5lUAkzmnkMGzQxSjQHGh1BA8HgQmDfhY8F6bcNR8KbsrncS1O5L5MpN\nBliNZM7fNs5tz+ppHkMYEK8QbRKm6oZvYnKWy/clLxfHq1PAb+NWxtGW4Y3Q\nhTA8BG6cGsTj7a/kCuq/WOELj9X6dhsEDvrgZ1pZTkb/WFCBr7k8RjJ0J7Sm\n9qGrcbccOQh9Vj4Y8lp+AmrmVtr41wkxe8+krZ/HAcXSMYPigCAcA9Lb1mdB\n1qoO\r\n=hw0z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFB1qR4n2hhnwGSX1kehwZhktK951ZkuhNE/w3N6nyteAiEA81rBpXUIufSNj/4RWd3avFVWgAzj2nJh1djuarC84ts="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.2_1580254847602_0.16705863496799167"},"_hasShrinkwrap":false},"1.0.0-rc1.3":{"name":"plumier","version":"1.0.0-rc1.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","benchmark":"ts-node test/benchmark/run","exe":"ts-node"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.3+4895e92","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","faker":"^4.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"devDependencies":{"@types/body-parser":"^1.17.1","@types/cors":"^2.8.6","@types/express":"^4.17.2","@types/express-jwt":"^0.0.42","@types/fs-extra":"^8.0.1","@types/joi":"^14.3.4","@types/jsonwebtoken":"^8.3.6","@types/koa-better-body":"^3.1.2","@types/koa-router":"^7.4.0","@types/rimraf":"^2.0.3","@types/supertest":"^2.0.8","autocannon":"^4.4.2","benalu":"^2.0.0-beta-1","body-parser":"^1.19.0","cors":"^2.8.5","express":"^4.17.1","express-jwt":"^5.3.1","fs-extra":"^8.1.0","joi":"^14.3.1","jsonwebtoken":"^8.5.1","koa-better-body":"^3.2.0","koa-router":"^7.4.0","rimraf":"^3.0.0","supertest":"^4.0.2","ts-node":"^8.6.2","typescript":"^3.7.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4895e92d72a89617a3d09b187cc4038b52b737ee","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.3","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-DvmNY/GehNZKr/KCex4ui+90yizs0jsIsGhCmftwk2KtUAiT7Si4b99n8SqsbqQRyFFRtvJycpOplv6p7Cz61Q==","shasum":"ccd9a7f549b5785c524db2cf515759b384b98a07","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.3.tgz","fileCount":9,"unpackedSize":20846,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeMg/bCRA9TVsSAnZWagAAqqwP/3aUduYvYgbB9uniFMFY\nl7uxR8QJdQrbUqYUuF+aYAmSHXn2f/HusAp2/ECG8MSAl8kOGll7Ns/DXlZX\npEZWHknQcUdD1czPUP5HHQcoQpK7QeBC/MNNbPlbt9SFyr8MHtDh8o7Hufvs\nzDiFmK6+3WU8tXC7w4O3dKHMA1dBLOdnFg8a9ulI/dsJ7y3z10A2bvB3a6ho\nwyAVznaO/rgz3MQ7ml47yeaso6jWgLeRUHVK/S1BfXVYRNklseDJwn3abAB/\nKSy6QGd7DCIlbgEW8TlZo9td8txIRJLx2Kkzu4D17kjK8e9K+QLOpeXAKpnN\n/Pc/m45CQHU6Ba1RqlaWnJrJzEb6LritIp2ZFRYX1ML5c7ry9hYeRpmxfO8n\nbA+aewo3n8e5+YiRMiqHl+/FjfsXE1Vc6xLmXN4WXfEpsBWQTW7637/TuJ6W\nu3r6C/gyETAG+7FrUEVwcgNh4Ag+JmbPbsf3cna2zAFC8a04bg3elDn3kj0j\nfJA5fmdHUoFBiEnrij8xkKPSPbR6usX9Mkqjn5ZyqcqBJ24N/Ojzq2KGiySB\nDjzjXtlD1LWTFtmdmK1V2TxXfkE+OJ4nwyf/vN8SKpRphdS3/lSOhNNDFHY8\nUQ+1oaBPO/knAsYIo/7g3aUumig6d3MGxaEb2cr4zZATMG348u+JUdOJOle2\nlbpU\r\n=UPs4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICI5tc37J3twOEpeZU5/IA2BQ9pxTyjjonRyh+raU/IEAiEA5cbU3bg9/UgGAiQRPbHhdJxv1dmRgVuPlmLBkCNwCJo="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.3_1580339162866_0.5854666109385454"},"_hasShrinkwrap":false},"1.0.0-rc1.4":{"name":"plumier","version":"1.0.0-rc1.4","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.4+2f9d328","@types/faker":"^4.1.9","@types/koa__cors":"^3.0.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2f9d328e9b8070f6c04c37b87b5282d1d48c3b22","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.4","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-DzSVDQXFYbrR2ZHz3vJYL9mwJcAhLq3vp/c5krJ3JfJmznK78HW3xTvIDm/njL7LBtIcvXHtp/43P35Xb5LKIw==","shasum":"e079bd565869dcb8fe8dae994ed368c0baa85fd2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.4.tgz","fileCount":9,"unpackedSize":19943,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeMhezCRA9TVsSAnZWagAA8BoP/ihYO3RvZAxgLcJ40sGW\nPvY3afTMtLDgYusE2Z8borZYzuCFXVIYZrP3uSYfdQ1urtp4/YrZnJ0HByvS\nb2wToJVoFvjfv2fQokjBSOTFkERi7gzE3y37b1VYHi8HtvRWb6Avt48X39h6\n1J+oOnMIAgBunksiCRLPh3zcnx8wc750Zh6ycx2WkMMm1gbdeUVUGz78WRrG\n1JCqbrTdXX5yO5YtAJquzHzNe0u2ycILFZ0nOYsiHNCJsXTH//iiXzWG9tCx\n1NygdxFYTi/JsE7gItR3BtjUZRC5OfmdPqjT5fv6N9GZJnafMr9dSIzcbZHE\nymj+8niM71V/v3MiF34EjAP2yJZ3M7mSyWIrvtcN8iztMDDbqqjbeysAPa5s\nw3XLe7hka0yxg6ULisIfHkqtdI2oaf/wlDpxLYc64yFH5E06zAUAw/+eTvBk\nOZglolrjQ5Xh5z8l/qr3JE+8UAFO4JV5ogSm/mwY5niEqCp3BgtL4zTa8fdV\nSx5oxnPNWMzWLgQdwn2tdXZ2kgeVXkuxPXg7Gu388E/OjOf3DtJ8bvfe+675\ndTAYSclEXltOvxdqIxrX3RTsCRCMxAYvKu5s6yt2TBHRv8cCkNrD6pDtr13x\nKAL0W105as5oxsqTFExA4vctnhI3DaUz/XLN/z0JYaC/ZHGCjwiDL/rgszYw\nZGSL\r\n=NVYz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC3oz6WuZ+dGl+KPrYPDNfK5WjsPvmEtego+obTXWWpmgIhAJT0rf6wyd9hSZt0rBhTcvbnvY5wGKMevB/jXO0DQ7q+"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.4_1580341171124_0.11728181253976344"},"_hasShrinkwrap":false},"1.0.0-rc1.5":{"name":"plumier","version":"1.0.0-rc1.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.5+9e9fb48","@types/koa__cors":"^3.0.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9e9fb48460fdd8d8de862c98537cc9cee427f851","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.5","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-IFxGMyURDbyvwqj9w6nCKLtWWlYsCPTxd35lN6mxWS38VeixEvLGda5OkQE8c5RVdDK/VYBb09xeK9lX9U5S4w==","shasum":"5d8828c83daa94f81c9231d49e6f01bffb0bd8dc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.5.tgz","fileCount":9,"unpackedSize":19913,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeMh7CCRA9TVsSAnZWagAAyfcP+wR5ZJQ6YjYWrhEiiqVc\nuSF7bZYsGhyRFAKNR8gKsphKZ6zuA2h5A0Llqvu7ZelfXnAG3tVAo9O8a/e4\ngMvUH7+xwLXDKBbfnFcDRJPTjr8bWgqQTP40CxO+t7QWIItoRMZx+ciSDEgX\nlE+XF8GnZyalcVhIO/I3b/n+IpnzzZr93NTEnuGYNkn4NYdXAvYpyGazsyJK\neQ7qttZA8OKmBCYGw2RG/Ks2u00fCTE65up0qL4SgwFzpr+Zdu0FWmg4EwXv\n23lE4eWGvjwHuuqg6DNO19uT1NvuhTKvQE/Kg43KFVWW+4GyCVTprIfJmUNJ\ne6ArXkDGZpfazX0icPiBv0BggrL0F/4mVfXBdkdQSnkZLbY/ToNTwaRVVJ2A\nidZ4owvzM7fxLFnIXtIfbIBqvoSgUTqPhLFT3Q/dQL5C3B155sHLB2yRQx4z\niK9ju45nzj370U7BHPpszYlzMbHL0JNGApEZ2qH3aoj9k/nr9vjvsTMxInMJ\nhy2w4jVxEPJ9xI1zAfKqwI/PJAMiw4Z6gS0is1Dn8Fiv5Y7VeJbhvukGV/Mq\nW6I6229bq8oBltiMvUKil76HPzHGXgBNgRH0l/Nz7jNEA5ET0bsxdXrLSAfz\nAkdY1R3aWg3RrMGT+NLUk2Ta+Dd0VHya86kTJCTtJeTh7EjCEGt+ILs2oAdW\n3oJU\r\n=Vbij\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICk24YxWSYz5xmceQIPYExcF9NVzv+RVJ5uxaAYiqY1GAiBlPh18fbVlXbZWrX+qgHEoTqdWX4GYtKUG7uqfPuBsHA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.5_1580342977852_0.6617155710654723"},"_hasShrinkwrap":false},"1.0.0-rc1.6":{"name":"plumier","version":"1.0.0-rc1.6","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.6+8e4c36e","@types/koa__cors":"^3.0.1","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8e4c36e25dd48af937d892f8481d6a84b9b0c3e8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.6","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-7AcYv+WHPCVtLIDYqc8rZkx2bA+VoWJQU/dndSRbdbhZFs0OG/FXZYkHKXcqAF+Va7LzJK8vpoHPPWsvoH89MA==","shasum":"2f30a242fdfc0b86257aeb72af95494b2a2403fa","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.6.tgz","fileCount":9,"unpackedSize":19913,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeMkVNCRA9TVsSAnZWagAAKlcQAJYc7fM+pq3UKJwy23L2\nggelDS4yh1fZyFe8SAu2CaOMw1j5v1NqSTguEIJ89KiA0JrAWPKENeXDDoH+\n9ZIxSXGiK4CFtKfzoa5upWbVhSMFFMmEUjH9rQaXO+llfSy4L97/TJ8GVR0N\nnMaUBq/rG4wqlAORLvFKtcF6Oe3aXD6QH2BpV8R9Ibvd0hqQNekuWyzOG9dv\n0OBovn24O9SXSRqLDQU5MMCMOC15+lyFmfGUJALddrMinZnWJfFY6lEzoDfV\nt0Xw7N6W8MEsIct98RnFqW/HDihbrrUkFv5PpxaxiaiR8CC7FAC4t2V3YLGd\nZQdiEiKnpDwakc8FTxeV4oMsFoYDpRjdGi+l/3aXWt/+ex/ArCC+gAk33sMJ\nmK52KlYuR6T9R6Vw1QV6um5BwgCNoUgx6ERH5QpKKzbhk4EaZHgo7moHCClz\nTYHVhtf4IdCs4MQsnJHojzComDEE0bZshXtEJtTlnIU749Mgy8VLBeksU7xK\nIbsCpDKMz98LM4My8cOJcVxhHIe1GfZYbuZNYVecBXjupBldzwUeGq6FM/DU\nRNpa3EA9/PE33G1rWIy0UrLf42nskYdqFtbIZa8aZcb9IB47b73OyrgmxyF9\nzCXPf25e8hB1zFexAInYWC2P+vEhYBM3IrhT8OmMsyhoU++RJPM/DYDSDUUL\n2m3p\r\n=awZX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDSQ76aHu5Aw9RTeo9HlBpqEfxWtM+oFQrLttOsMJxB7gIhAKPJ56LL+GqEtiXXVr9SoI4yVagJ8dLgwhQIsD1dRldX"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.6_1580352845275_0.36392080535971094"},"_hasShrinkwrap":false},"1.0.0-rc1.7":{"name":"plumier","version":"1.0.0-rc1.7","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.7+cd57e3d","@types/koa__cors":"^3.0.1","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cd57e3d4ed47cc5d2f83680fbe73a5f2f5a412e7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.7","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-I3EgKtW7b9ztmgC25wqMR62G00pZBRE32rnIwvHlvW3GYXeK5WJ+8hOq0ZZ4C7B1yAKzQAc99RsHvrnWlqQuBg==","shasum":"f882058bcac0c99236e6bf0b2038ba3f2d519fa0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.7.tgz","fileCount":9,"unpackedSize":19913,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNA7RCRA9TVsSAnZWagAA26QP/jow7Cf+Ff4FUbVdPDKz\nKxi8MFqQnw06moYuWm1HoySFUE5VnaaMCCkKLJc881kHg0I1o6ZihKx+//z8\nba5uDq0hcYQidj1i/Sia5t5T/aQCb0SHDOSv0sF7/aMpgYX9Rm2cYuJZ8kus\nyQz1yivnsLkVt/J9C7bHE8umGTY54eb7FJfMzHFHQ6XWFKhAZPfzv3A/w4v/\nORWK1EU1mRWcpk/C30LBO4YnECKzCp8emeHPb4K2pNXp6B6lSBsksekdg0nN\n7lL8peU/oXXlDTRXxk82tH3TXZ8qKMjmPs8Ctbs5E6ex7RP5nrKz8C6z7uzR\ndgiYi4sMn3PtVrhFh+Uu7l+YRUOsE3bJtia53hgtLiB1oOH6kfgcqza3PvJI\nNwUV0cjnQF+oKiUuUMBZvi29I2gvBmv8LKYHTO83wZ3rtWm5am4h3eTvR17g\nK78+Qfit5OVcUcMfbBymnBBOBRh2dkz7aQ/lkNX3jLvc0aUzBdJLkyDaFBhK\npp3wyoqKmjiBG+jIdxeG+jtMMgoz9o8cUJntnS8GwZE5S+k1y0+xprBKYbhV\nyiWJbPWGUnD2vGdDKaM734EI34xfdwFvjRzFez0oVxFOet4vPfQaU7fyv7se\np3cPxPVAzTblwp1ZVtUGL0asW0tGmaB8ZxyyW7lWKQwOVPciaDCfaZFWzQ0A\nkixD\r\n=6ysj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF9J7CTsZ41bgyxXk+o+yuY8/1Etp7iyBuiVE5uXkgVIAiEAx0aubLUgrypNQFXty9u6YedQEq5rvaUHAQmSR0KynfA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.7_1580469969282_0.8820889760331316"},"_hasShrinkwrap":false},"1.0.0-rc1.8":{"name":"plumier","version":"1.0.0-rc1.8","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.8+abc0f69","@types/koa__cors":"^3.0.1","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"abc0f6912206cc82dc2e7e663362c103d029273c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.8","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-QthrLaf0VNJHQq1aUxEh5HXVo9L5GMvzjvyCub4mArkJ4hC8e9Oac7ekbnKkKkT3ZkA3JdcwYegR9cmzSA+HPw==","shasum":"c57403d319b79baf35c9e8aee46acdd3218659d3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.8.tgz","fileCount":9,"unpackedSize":20059,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNVcQCRA9TVsSAnZWagAAD4UP/RaXHjtztpszC8ZImRvx\nRcZ8xwSuzVaLf5BQFxN4hnG+Cus37aq8RZluhukovrmctsTKeiznd6hiRCy1\n3a1XzmwsH41hH3HLB7GZIR+GWmSRmu8auHdJ9saB8+rTlJXQ/r2ynP21hKxp\ntCl0FAUpYlm4CcglL/OKJ0RItDwMtNpzUDOvaClzp62aZE4QhjYDjSd2IE9o\nWkoSzr2BnRM7LuIo/BsqHl1LpOuy3Wj8QTXe+yRH0NTpbBnO+57F8EfT8Z+N\nGDYHBcthD7WEosgeRhZtqeYwnGkfAlidK6VTA9wGfJyA6aoPsjziOObBWDQR\njzoXYFH2vLedeMbxT0ZW9Xp/3tt5RksmpKbZULVNiMuUECNc6QKAwo/0IgZk\nlopeVqWtbOlg0HcXDGbI2sSTV18BTlWtD1zhDdH/7EfqAc9S42Fe2Cyz0hjk\nAx6Od6qGnUBAoYJ/WNYr2YmAumpy8HTxS4QtDv6zdVto2MeGd3FZlE/hKZ25\nMA0cLlQdc5g+MOF167ELMi63g5omqPbkMz5yrYxsjXQkhd5cFP+Z/uRdl1fm\nj1+gfA1cMl5N+OIb2RwqUwFz1BQaHweW8xE9W57G1u6mCsybfs9azWc1or56\n3N0QWfmT6HmwM+mfBeuNQ7Hw43/BhQCgIIzq3quOpPjuUN5DBYszYqcNeNCi\nTeMH\r\n=Oxwd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5AMBwmU2ZR0sN1LqMbSMShR5JRUVlF+8mMmdGVsVlJAIhAMZgFU7t359q5J891LNCx+Az/q6evgyplhnpq8AklQxm"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.8_1580554000009_0.4538939555548358"},"_hasShrinkwrap":false},"1.0.0-rc1.9":{"name":"plumier","version":"1.0.0-rc1.9","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.9+8996a41","@types/koa__cors":"^3.0.1","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8996a4119ea5a9d9abe8e0a07e6fc275ba7dd38f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.9","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-quEnxS+ZGJsIQYLLEAkQrtvoGqQbLnIndR1qRrHoehH/INi49X6gIoVk05M0I+SZO0fG8+8K8cHyzp5eyiSWyg==","shasum":"c11f5f6ec83d8708ca22bc333a305cf2431ff63b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.9.tgz","fileCount":9,"unpackedSize":20059,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNeB1CRA9TVsSAnZWagAA364P/2yjugiXgwEAvMh72Htm\nGeAPF0deesXpp94Ch+0x4h29nAYXELJNzOJB8bLN6zXpvGxGPCwG+w3sOfUo\nNngHRU0i7UmH6t52LXpnmC9k5wIuyinFruA1mlN5ra3glZO8/WRf0ZvvOJcl\n0TlKVX15qmpJqa17DPt+xHayD001faOun+h9VlMVuzHIQ+oMwA8pRIPjKDKE\na2Tz7L5nKpQjQWIOGyK7nnq3vb0y7kZ9hEOUFet34GfDkU5o+pX2PwrI4tru\nPl3fo7wQ48sCJ7P0E9B5+Psuq7a98Etw7cXdir3cM/e15VXTvhN0Bk70tVGN\nZ0xOKGoju7jpuiKXrbedoPLUcm47k/kgXTnYQ4KtRtDOIGOWLiglsMfM5k7V\nnKxTAs3pkvf0kbvz8oQ0+oPIFxREGLrl1PjggkSx4J/IsQkDegJDPMNY3obs\nFf0gt52GK24oKCTd/jZdJeWgHNNUaqyde/NZtInKQcJTdl3q2LxzPk2mNpHr\nZxF14NghEfLGSPIjmUqWlERU1f6gE9ekdz77/SuY1f+3MKpWiIdvxmu8LMfR\ntAhK/B0HuUdzjQDl68EXx50733xHiojGGpY50ZzFoq0gfu+sYdZ5w2kvefam\nvuIX7tyd0EH3mhurUClr8cU2QPRI6hWlAve6DireW3J48HLXRrX1PV2RmNnL\nKLFx\r\n=v2W0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxy/w3Zp11TJ98yRTYyA86oyfnbLZ3Z6cAIAdfHaGmRwIgLG0OAvXsRMfq8k0gDg4UEWSTqvk4DmRJPDqL2D/RKmA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.9_1580589173458_0.39672420643195383"},"_hasShrinkwrap":false},"1.0.0-rc1.10":{"name":"plumier","version":"1.0.0-rc1.10","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.10+00bfbd1","@types/koa__cors":"^3.0.1","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"00bfbd1972396f5f6eb50a4032bc148c9a35f4ab","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.10","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-qWvmsbRVos6Q1rYhUnU6YaDEds51JO7S4AfgXrRfubLNvkm9bLwglyOAvNIX3WvaFX+B/1TJkycQhXlDpE9FJQ==","shasum":"0906d104b9265b699e7b67c5d690d86893bafa7f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.10.tgz","fileCount":9,"unpackedSize":20061,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNgtjCRA9TVsSAnZWagAAhysP/iRjpjRyqBYDre/HCo4z\n9GKHqPtn+WiXCXCUdaA8NWSPq2v/23c3/Z9bIQ+4UM54I0lNneCrWg/vzEBq\n3+gq5wmG/kUkxQm8D+cYZbxqgCGh8zAWLLSdrouwJO9d7n7Q+SdKTXIITbsK\nyPnqC/qw+82ZniPl18RmpaOvhDqmxg06sEiDLrwbGKL1n6vfdYtsnEcIOwFd\nDdmprY5lYDs1UIvdznQvienEWiFF5AdsCJfVFUaG0RQ9PRJ1ivfulnW3SHaB\nClpyJFjbrPlh+uKp3dHLRphMVMnBpyzlXOAxi0RucDWa22o7akKuw+jG7WP6\n2Hq0kU0BLXS9kevvQOSP5/D4Iz8Vnh3+j8tYOrz1QqKdeDWGG/NPH74VaQTe\nIskagPwezWo6GQrqtO62TQE7JPZbxGICv24qnHfMYoR9lFLBgh6UKEk7vwEk\novEo7h8FuPnC7puOeK0uqoYzG+739w7kwjD/ZCSgjdcz4e+Fdi/b6W8UIc/u\nPzBd6aIkQWhpUGlE4k8inPuGJF8iTzT/fEQNIyBenoxloNc28y38CVHLt93g\nAmYbO3Ow+Jg0h3jSqzATb8XNjeA4tM/bPVHbl3uNgXP65m5YVaJiPEUP9buh\nKoyPQpOk+/+0VCfzHdhN/yqLDrP67fHERrwFSASzM4KGSNjY76R2+zQd3zBd\ngto3\r\n=uVv8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICRvudqq3+FUg8B3Xa/Q0rBEGKwqRjr3WIkcIAFD/C9+AiAP/lG/hn+tk37fWWMM8JHx9o52Lwe7yEL1RciFblC3uA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.10_1580600163207_0.9778841639669216"},"_hasShrinkwrap":false},"1.0.0-rc1.11":{"name":"plumier","version":"1.0.0-rc1.11","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.11+a43d92d","@types/koa__cors":"^3.0.1","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a43d92d6bd152292fcdfbb8ed6462826c599cb30","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.11","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-xKIsyc1mIrZl8kFB7G14D80KVVBcrZdGmoY8kgrMom3yIfkDZYDHymZhXeaY/M6SfenEY6IJSn9nmpzuu3OMZg==","shasum":"af96a21ba67d951cd140ae3709d3e7007cbad97b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.11.tgz","fileCount":9,"unpackedSize":20061,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNh8/CRA9TVsSAnZWagAABQ8P/jtbksPwYxsdJBYOiFBS\nk9V3NFlKDF21mlPUN5ejvMhITSMfW3pFSZEDZsy6MYzRNi5DdIfDxmYxoxxw\nEcOTZDM5L/w+7xP1qJ/N01vg5H47yhRPzJWTrQoaXbOmdOg5wp6r8MNBygtS\nJm+S7vn5ADOUNtN+1UCAcrRDuNQdVY8SZvv1tVLfvEQd4830hV2av4Cdo1Xo\nRyQoTpM+2TE/qgCED7Ux7TqCCqGb3VsAfJVF3tykN0KNYKsLJaPtL6BMZ85i\nXMvZUnK4Yh2TbYOIdSxe3jvjkw24ZVkda9ueV1agyxurljK6hpJoL/Jr/NYE\nwiLGVHP91WN9Jm6Tdsxz3ySWeB+z6mCreCteCvVgjDRp6MZX5uUNsHQ2sZ5/\nWFkpnnqMHkOyplyHHSrLzGhkKsmgmbvxL78MB3Bl9KLDyFXuLHokWvk4Ray1\nIGv7aixPs54Tt6Zb1sAINzN8My0faT9OPVkyfUA3RKVw7+Vcci10//bQfBjh\nvfaDLC03f2YMwoEWYtPi0n77l3PlAExm8Dxfj4QbTebkzsrModdYyMgZ/+c4\nBfhv5LRioIqFTUR0Wyal/xjnIcYF/vNWxsXw9dvhzTKfndVN89SYgpyBq5iG\nuKg8LAAfAPF+1kadGtgsxXGPI8OMi2nyyp9kZAp2qRziJPnJyxmMpvyZVgIb\njyEm\r\n=Kndl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDrdpJg+dyrMm39K4N6xpCjsLDdHp3D6fNyUO4IbkJ+7AiEA6gsnP8qswLOTY7B+v1tkzJK/SvOuP+Rt1TDM/i+IGtQ="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.11_1580605246553_0.8826057282741464"},"_hasShrinkwrap":false},"1.0.0-rc1.12":{"name":"plumier","version":"1.0.0-rc1.12","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.12+bc8c4b2","@types/koa__cors":"^3.0.1","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bc8c4b2df4f8d88313d6a2be2ecdfa3c4c5ec126","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.12","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-WHuksVPciwHJFjfshfbv8ZbU4tXY/5Q+5pKlyymDgDGlAXd7XUq1mKKvBnj3rYKR3x4y7v37lmGGBiHwc+FakA==","shasum":"6ac5cc3b1282d342573946260f7dc099125f2803","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.12.tgz","fileCount":9,"unpackedSize":20070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNmRECRA9TVsSAnZWagAAGucP/2uCIuJxTCE5PQ2x9y2J\ndw0E8u6QGPH+f7irf9Va1Mlq8thIar+yw1q6CYzDMe1a60TB5/0vNlG/finO\nx7Rv8XQ3dnOy4NE/Ca/btQSQcSW3Ab+YN4gznh6V2zCRuc+Oh3xVK30Ng9ag\nYC2AEkXqrttIozUVsEjzvPzKfsoXiyOvcp/J8RrNGlDRGqxjVjZvumGqdxm8\nXQXNDee77cPFcn+sdFLRcDlDJEFwnjaS94RVrkKGhEvDT9V08d+tKpEHyIEa\nHKyrw6eFdUmKMxVSj93jCXgosYdw5062aSW6tEHri260mZzgMtGp8xC2saB3\ncEpKu9zbChIb1A+z/5EXN+ofMnLJZfw3+K4KjxuyyzEjmDsuwHTqUx2+IBuw\nSa4b+vppm1niPlJn4pVR+SwmXEIIpw8R9OpY9PEl3fXaSEEaBxx714aCcwp0\nybUyDxyVKjCAJKfUvgtIKQeaFsPq3MuRkzGjW/F/oRBqkPvWbTbPJhbdV7ms\nqW6+WVk36H9N7ce46+QGtoA9X41yRpIOSkf1D/B3dhRDtqUgpqIEI3KNUWGo\nzjqwXbrbD1D/Xa9oAiS0smmyCGMhKsyECdA6r0G07QVDBXhfcA3hEe9ALHIR\nzy2yXpzhjzFJslAzT1iLWKsigeOhsZr2VlMrm7iK6JtyvCR3/yStob6cSvmR\noahn\r\n=Gcy7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDT3umxtf1stb5cnwzrf+pciM8rY9Y+vH7UahZAodVCEAIgCnOTcShwbWFda/xbg9cMBpnLkCJ0fDx6pkM0BQTtXTE="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.12_1580622915813_0.08830787053780664"},"_hasShrinkwrap":false},"1.0.0-rc1.13":{"name":"plumier","version":"1.0.0-rc1.13","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.13+7251fc7","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7251fc7712af17ac72362ed6462266d7c3dc2bfc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.13","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-rRnYqlTWwndaqy0Asy5RvMeEEKJxLOojcdJtweVCRPbmUTxumWhruPsUmZEG/qOx/pn01kyrOjlR2QQcTTkJgQ==","shasum":"468707352ebf881f0a612eb1584c6de6bb3412df","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.13.tgz","fileCount":11,"unpackedSize":23014,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeN0/8CRA9TVsSAnZWagAADXwP/jhoqVZxF5qnM98xasas\n+S8ZJa4fOM8MStebz6biSspBi3Ke6C3NgxiQQEv6U/ElWAZQr1XItyGpFQyG\ndz5Aquhz0Arz7w0QwkBCgTBODYv5TNOO8pQtFY8ps0gtGnBUCG2/pVF8sG8s\ni5YaTNmJm0GlN8h5t2lVh8QtIOVaXklmOMfs3d+jSmXT3syan+KtQtMy43Kv\nZDbfnZHrkkO8cZMBX414pTVD6wXK0+EGx8CQS+zSOGlZFRF0Vu8BarfsHTri\ndMwlmpbZ4v1U3y3JOAcGOX93LcHxNNBC2ehunl4QdEpZscVKSsXYKMlqL4My\nBIhSkDF7R2PtOcoCpf20/F19lhrbwnvgcPPWEF/uo1fObWeWCREHdU9ZZcJq\n7ah5HYtQGHmGGePTcz5871oC6DgGgiX60U9GGZUKpaGl7mR7JIjBy0Azi/HZ\nY76itJMhsRJixl3FFdQWLjKt/nnb/4DzqJErHr427suoKvFoClHbA8OJKg0d\nrlB5MHayLc7uz51pscMEExcIpvtE1ecd08GBInuHWlNLoUOfXxQc0kCgXZpQ\npggRklUDqtwY6cqQrF4449jVwrrqkVdVa6QuC6N3aWyC2vTsxMQ1CR8bApxS\n+IFaIHKjwwy/rFJIPBPiGA8JpaOOS2JPyWIa7Q2e3CTDsjMa3md59bjBZfI8\n5tFs\r\n=3ETB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCahGNJDw9xug03yyyYaJLZSH1oTOxhwuIoWjPwbLAmvAIgW4s1HRLCLLRzyDqCgdnZ8A5iD9DqusOuzkpAGBAGW2I="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.13_1580683259928_0.4350734758020689"},"_hasShrinkwrap":false},"1.0.0-rc1.14":{"name":"plumier","version":"1.0.0-rc1.14","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.14+f493f3f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f493f3fed522b1c8ec2d76a3d3e12c5d7f799bb5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.14","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-h7gHL1NtMAhBCl20A1hZ5shC3tVIyFt/pXE0rKmqLIq6OwLeqaoNbawK1OfopBKBnYbDYBCtLl5t8SMZJ0sqBg==","shasum":"9979dbc66bbf5e41b008dea3c675f2a9daa16567","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.14.tgz","fileCount":11,"unpackedSize":23014,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeN25WCRA9TVsSAnZWagAA354P/39mRu4GH2KZPLaTNISN\nhf3DQOQ++uIghhYzMqFnpPYludPS0C66tnz83gdfTfdh+RNDNxlcKInKX33C\n/t68ZsKyiuOMCoxT6JdBlfeB0lv8x2N9owfBXZubZNV96EejLt2WztCbDcK/\nvoDliL5BACDVY+rsUUvB3x/JpAXj2LhZuAK913Z+v+ZUBt2UCiyhLuEvQRlF\njUv0R6p6m5QBYshUwlDedGFEmGOvjKUJrjpaniQFsrXjVjaYOeJ0y7w+QE0D\nZdLDKMpJ88Ma2N7L3A7e/M0nzinO8YC42EmmUQ/Le5Um0AMbt5kjWzMLIYEH\nnshi/vAvlvsvqxlWiBwm+z9LpqBs/bTmpviPlUOQJkFEVp8Ixnd8CztA+c+U\nm9DswRXF9pCb7VuC4b/djmjhM3xEYckA1eeGsJhZYEASadHuS/vlAuh0OhZQ\nlfFNkJEfc0buHWnc0cvomTHC6UGPznlFfucqUHSO10SSmaT1gQbxBpT6cDre\nMnE7htxJFkVZ3XBVbjAWIBDnDneyoxLStdi+BTlU6b7y5OEvboxU4ZSUWDGd\nWRhdwiT3vA6gfF8J6P7LT1WzzsSMp5h/W9wKMOMrt01hoi++cFVVVkRwKQpw\nI41h04ofuJnmrstqqKmnKUe9CENjA3jrSia3DAda8liL4/jgdfyd38vCDolF\nIEnF\r\n=qq30\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDujv64znbU/36go432dveC4pdv/bFxLTasFZzLlngtNQIhAMhLHN/kNRP/ya6hktfEs1apP51BUryy7quHeEkt+glK"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.14_1580691030298_0.5332445791752751"},"_hasShrinkwrap":false},"1.0.0-rc1.15":{"name":"plumier","version":"1.0.0-rc1.15","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.15+ace1c3f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ace1c3fd13180fc6ab4d69234a1cd15fd67c04ed","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n\n## Blog Posts and Publications\n\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.range({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* TypeScript\n* NodeJS >= 10.0.0\n* Visual Studio Code\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.15","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-Ul1/QweuYKPpVcS93tic33n5ZSZuaZ1hgRo50kgOJxvAKvk9TJVWlRS0AEFT8WCPoiKOpZorwIvA1qm0i52iLg==","shasum":"deefbe99d31a99024a5ff126e6112ab9865f44a6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.15.tgz","fileCount":11,"unpackedSize":23014,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOKCBCRA9TVsSAnZWagAAHd0P/2nspt+q0ZFp+3Q1ds7o\n7QVC/yxQNCv44+crNPErl2XlstCU7hrYL3nQatIIYU2HQPCtJ/twMwxHmfXO\nUk9QbTfb89L1tDI10vSl6W62AN4d4WviFUEntj/pIsRNawrb8GJjS5lBDIgx\n7/l2LNBvjVqjtJh/kHsDzmX6jEIvZWJfaJRhNGs/PLIKjfowSxd9UEWAIPVI\nfCWG2RjkCGsSVQFP4/WirSAPNnrooMq7lmHUMv2A6LcWT9kOVqAMGwGpBN1r\nnypYfjbtUWLkaxyeoEIzTzqq4a83Gn8/k94TruNiLZziuog7HOYo1qlQ4N9A\nB/GgpAkSdDnUPf2RiLWKE4qveOevmzQvrtmkf78XBeQ+H8oWbEihWtyDd+vx\nStAWeTqsbcSQydvG4eLrwvCnre0z0HV+kKQnbLn1Lnl/goP2bGIwz7MJ8ePR\naQ6wfoQz89ADCXv4VfiPztbo8lVbNSchRSCYLUW9EZdyPZtm5Agn4IU186qJ\ncB8CiaLWM61hYHoetzzyBaKbaEFb2ELnKVrBl1dty71rQbTd1lOZWVgp6NWp\nd8vJzxr8i5+DtH1d6dG4taQ2IJom0aJvUbQsyuQ0RZJ363DAQKvY7rEIM0Vw\n6Ktple+2bomRwWo9KYY9frM8+7tKKga/OH5O9AnMYL09QoT9Gv3LVjBjyI1J\nBIaE\r\n=VQZe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDecHnsBx8AGBNke7KRejWgDZLi2EGM1CvmrZIJrTPgRAIgdX24Em1jx7SE+pMw0a8g5N1JzoCMc8h4fn43QttrdE8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.15_1580769408993_0.6745159034859611"},"_hasShrinkwrap":false},"1.0.0-rc1.16":{"name":"plumier","version":"1.0.0-rc1.16","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.16+b28f9c9","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b28f9c98fae501ca5e85c62a89c8492446abed46","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.16","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-QdJUmuoDfGGQoKP2dp3drSHRlmaRvBZoYdPtscj3N82BPZKxr9Rj95jycmdIs95AFkqsuuY8tf8F5vDpSvTBEA==","shasum":"c2c773b4f2b2c1e854b77d60723162dd67000329","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.16.tgz","fileCount":11,"unpackedSize":23112,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOKQ2CRA9TVsSAnZWagAAsK0P/jPp6um0JQyJ4zj9//ji\nAPgrHOPQzgPD4V9JXOsvgq/EyIiXCPizO/zn0yCF/f3/letLwb7X+xbjhL8P\nglncgM/CDj6QNhOhohzgHlEiRZXNTjMC1nowIH7aDriFFFixE1mkxTW1xPu/\n6XjEz9S+NGgG2+nB/vforLKKyugOE0iBckp9erLAAMI2zp1M1NTLT/kC0v8o\nR5JCufeLH4BpxGVQl6GjWaXZ+JYujK3oDMn11cACXEjnwgF9uGHlTtdJK8GL\nr8r16IXHcEaQmyW6C9107O9wSFd0UhqypsQtH9Js+3wLnzsFNY7ACDhmWe3z\ncgxEifMnbckfC4TqdDXzbXUnh6qqo2QqvUfGOfDW6KFDh8ybb+EUrm9OG1rv\nH+3fh7jlHw+5oQkhLK6R0zYNSaGOr/odWjy6JVA/Ae7G5G7izZqzYE2+9Gfn\nd1HryZNv6b/S6BRuZZqgXgq7yMGSed1NTzGmZuhxpWA/t2DgRkdnN3Mtzz2k\nc1SSesRDDyYaHyGSkNO0woC2/AaB2z9NL0pa+SO5FMBgraEgsrlNIqBjxviz\nW8keDsA0nxhdHj3AUUxM4jfSmeO2kedFbYZKqXxQI4UYHVOHM3HSX6MmHJpU\nprGqSjdVtQyyCptvBY6ZEH667/qJ5U53xO7lADEMXfEio13bJejv4CdEDcRL\ntZbx\r\n=15GZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD772d66DR8S5OpTJig2zMVeIPzoKZ4B0q4DWo8Kuj1UQIhAM37hEDtF3ZZ6mMCnhhlGkiDXfC5l1+nSIrcFi84NOzH"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.16_1580770358150_0.49862858144174393"},"_hasShrinkwrap":false},"1.0.0-rc1.17":{"name":"plumier","version":"1.0.0-rc1.17","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.17+4a84515","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4a84515278af52f3b223e12663d5678230f3cecc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.17","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-GQsVqgfOEpodPNjO3ENZOy4zhqQIH6yXabe3F1gQBYk0JC/lws9zkvX2GT48oj+EH1vgNWJ0h5LStYW676hpfQ==","shasum":"344d7c2624e10c5c6df80dd98d9cd365c068356a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.17.tgz","fileCount":11,"unpackedSize":23148,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOjGICRA9TVsSAnZWagAAcXsP/0dqVDVVlgtBb45ZJCvg\nbOcOLfyiNZZlAMm+ohhVI1+bi+CxKPaBpX84eCMx4jDQQofc1uaOTyXEP/dr\nimDTXtmudOJl5N0ta+OENt0Tpx+On5TbtwpFB5L57EZ9tpAIi/3wxpLodlia\nMMkjJHXIJRNu4AvJ/o1+OYkBj4LNpUcXCxLvXx/kuS+4H8PUfGG5NtKMr1UC\nOqLcV2uXXpupjGXXvsZPZ2EazlcdL2TF2IOoXhmi6hEAYjd0dn9UnjWb1KHe\n1DZgcWo2Yb5ggmrduvkK7MByxSEvSvLXReoI7WN/PtPJ2H8O7fWF/1yjaaHp\nm1LeRk6ZYfcLo5a3FKuO5RoHXg84g1ldd5QSKjx3KIhT/5Olr2leBUhVEJfk\neuUV7cDX9C3faSE6DY/d1aDxgDFIDWuyA1o4PXb9xQkTKZL9Uq15IB6By3lh\n2ZKAEb+F1B9P2YkWGggL/ZaTArOFg6Qb/dCWVHDdY4UnyP9+NcDIAk/HUN/W\ntfxsgmdGBTDcyjfasqxhrf6tyyCL+XIXx12CO/2IFAr0L7Cz2gWf1ENUKweC\nCwvO8otDagE848uLtecMRxcF8GoyfFQqGxVhiL3N53BWpP42+1AcWiL6VYtS\nyXtooNkW52xUohJ60aVWk8OUpeQKnxP5brHPFhEnrxLMi0isNhzqhAW8y6t3\n/WJm\r\n=9xA+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEVjxqlr3wzdoBZdrUmpULJIQJHvReApbPyd8L4izemLAiBYUYnXFGzsyPOcxg35YHWCX6Ma3csSsO9E80HFj0jsHw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.17_1580872072017_0.3811203319555043"},"_hasShrinkwrap":false},"1.0.0-rc1.18":{"name":"plumier","version":"1.0.0-rc1.18","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.18+e4f93a2","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e4f93a2416def987d187b66061fcd459c1755500","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.18","_nodeVersion":"12.14.1","_npmVersion":"lerna/3.20.2/node@v12.14.1+x64 (linux)","dist":{"integrity":"sha512-3Dr6fMOi11pPbbTOmFMWGzRDamh0VdICn9bJPqmnTWe6BU6moP0GiCkN+fkMCzkU7P68pI5+hqCIHgeVSzZc1w==","shasum":"845dafbf844fc6047e0971229ecf7ba98279bf6f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.18.tgz","fileCount":11,"unpackedSize":23148,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOnQgCRA9TVsSAnZWagAAXsAP/0fz8E8TGos7WYZ0g4fo\nNRJq1ghvGdZ+VxfCAihJEWK4E1Ubf6QWXhWgyuTqw9d3gPAOcKaeXYhPtvWU\n1b7G+DbF3NxMYZjpJSVuJUUCjGY2cCTbXvDv7aW+4JR2SdH/Q8mxXTiI0Y4F\n6rmkI32ZhyeLm+KMmAnMSvJHg8tlanc1d8fTnMD1AGmewAKk4kVw7bmUYtKP\nPaFLtkhMCKVQUOT+0O3sdWRzp2Hk4PpHWsaH8EKtGunFHGfY9fLBAijcZ6pA\nnbXzoJvvGL5XG/OVl2RlnJhWxK2ZsyOU30zLA/zmj1GU9xRTwg8q5Yy4UmIC\nYek2V+7CCJOuQGjU8a7LnS5XtlDlSjnRNxMJAhNhQ+Af4SYLlSb7IF6IE4Ao\nGDptc8HX16KgYKolT8GSJ6IpDTqAol/D7rqjM2a0tYbDDvqLFBOXI6qvXbC8\nwsOgg6qSXvGHM0vWmNLbBK5D4eL+745N19g1Zu3zyIvmD7+ju6QH/fpEmg6T\nxrTsAQTnWJVXRga3m5EAXulSpKxfU6gBx5QLpXCzHkGxTDJ/eCtTv0hql2bX\nZ1tuGcr+K8UM0KF3DmnUJxxidF8nqgIn2mtQJ14CBDV0ug9Vtf0UWxNfOFYa\nzLoSSQzwVlUKrlH3b8/eYktx9pULSMekLVN7QLrR1SrGeqWpoXr8HkE6RyRB\nkhLY\r\n=XYcq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB5M585jlwOUUjMs7K2FvaFsz6gJJLpK8OJpZ0rEExLLAiEAlARSE6yuxHc54O+Cm59UFDrlSOh7Ydxus66Q48tGb4A="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.18_1580889120222_0.16241304214311847"},"_hasShrinkwrap":false},"1.0.0-rc1.19":{"name":"plumier","version":"1.0.0-rc1.19","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.19+a09590d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a09590d86b0742b295ba4a1c5b27c806b9366ba8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.19","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-AElnZdqswCMZrMwjHP9Mm2JiKwNJej2Hwoxy0xIReasjO4EoPEwpp0E0qpV53XpKL051lvF0SFsQciXIE5rgBg==","shasum":"555886f1d0d2862099322508e43e272b04ce27a7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.19.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJePxs1CRA9TVsSAnZWagAAnb8P/0c0dnSqfb2aV33L4gKK\niPL6rw0QoeSG0PcuAU1A1+JNqSi1wop7i30G5KtjfiMVrwmr1lrm9ZVl6Yq+\np5TplqMC1/4jP1oAbh5mM/imqwGtvPEkaQuGLnCBWBCaEkp+nquttc+hBV8c\nYQRyBR4oCVO7pg945l+pGO1GnDe0DLL7sfj2H3smG697yau1bK+qtK4mv1QA\nj4xilxfSTxFV8U06EVuhDy40DlISwcEnzwOtemMeB8NXRpjFZ+ItaiHRB3fO\nayXt3mV04FVb9yoc5k2cx+QBHFL/cBZC5J2xxdA76R09uzLt6nCbfsBU16XU\n5E9PhVNIFSP3vCsg1SlofOh2kEBNebhkkgsuGH46v4KdPf0fh4H74LZry3TV\n9Xvn3tNRhHAPP2GXcREuOr8V5glF8aVs6ZYKD3tbIYSyktZasIYenqpLLqr7\nxzPHIGQqOx7oU5YtS37bZPPLmhVFJPoTPbYZdJo4sLg63PpjVrqXdfJpEyCw\nbNUGtqAg/aTKLmfRH4kZ0Vj3CmCckNLgJ+7wlICjPvSYg5CvMvdcoX3axagy\nEfT7YLHWObacMZS+Sc5jjMoPEYe9sj0tXTTp5XhddBSdl50ODXJw5lILENhh\n61LJi5V1g3m8cJ0oMaceRE6c0dVzrdtcXMP5jZIYsfFgQkZMWQUI9xRXLeHZ\ngKT6\r\n=ykg9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBiEPy+XKOJ7QwNKl4ZfqPWLkee6xfCojUU8gAwyMJ59AiA+bSvm7BOhXlgoOzJIv3RHF7n3aw+QWTEXmlZGRzFamA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.19_1581194037052_0.23985642696510823"},"_hasShrinkwrap":false},"1.0.0-rc1.20":{"name":"plumier","version":"1.0.0-rc1.20","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.20+c3124f4","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c3124f4b33ccc856db828cce6dfad07bbbed4607","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.20","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-uW0l9FCoVDoaoqWNpsJbMKBe9Vu40cnmN1WDCb0/iOPBT8lJ3bbcIhABAlVB3DxbE1eAbbMyX1i6/HV2/9SkJw==","shasum":"d2ff036e90e9db1e91480105c49f754eebdbf74c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.20.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQaoICRA9TVsSAnZWagAAzAgP/2DHgo9WyKB+JF2FkKuY\nQPalz7r4u3Dq6p9SQrRBbihKGSHvmZh2aFFhqbj/gsW3t3rHWE9NnG1ok/Cj\ngnTd9sLK+RhjAfJWni1V4xJIL2J/1IW465J/dlwP+ZfwHrgsY3S2AAycsxwr\nFsTGyXBFnTJEqLMaXrLjhBcD55ucirsSB3X5Lk9crBhdNxtWpgt82Nkcx+zJ\nWWYRCcJxy5c5A5Jf9o0cT14Y3LJeHiuih2/ufgdIxczC+mrW9fZo2s39Tt+z\nhoCVaJvGIzS4MVaez0rhTQvV4pUm4xFTMcrHxAsmMLXUVh6MtQ54mH8t4LLI\nG0lbtz7XCGpTBOEifmCBzHTP3zyZ8BIR/IfOSkC+tWyDQp7aAgeyQvz1Y3xx\nLM7/KFYrgZdltspsqtvSNqzMwjpq4GhpBfmMboS2sQ0hsxUuNYs3G4NFdjI/\nhsegcuq6Ya+gkuBS2EkP4LnhUrVJPGsJzdeQskxwPY7XTspj3cj1FX0mPHW8\nyhbEsF1QYCLQJeh9rx4Pev/kkKVTW/ui1NoD1UyGPFw6ZjVTJ0V5wr3mVK7x\nIwpxzDoeONlvGqNHiXk3tiCbwWIvxDX0piuzob/rKonaaTEj1RtCTnXeXBox\n+5SghGfucKdUk/UosmQAIJc76eQwNKvkm20reoJtgvCi1wAR9jG6yGlRBv6o\nUpV9\r\n=yEKY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA62Uz2NOFtVTLw1I597Y2Qxk3u/+ewlo1kibk8Y+UKVAiAfDfCXYqxP4h5oYomywTVaTGJSOFiPFxJQVMTk+SRJ4A=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.20_1581361672358_0.001086656223761917"},"_hasShrinkwrap":false},"1.0.0-rc1.21":{"name":"plumier","version":"1.0.0-rc1.21","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.21+ec9012b","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ec9012b6de818192b52ef0e684b6147e6614c10e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.21","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-osmtPuHEOsUyCv7wcWLJZKjsZDFXrPaWRnVkqZyQCNHAyx3jTfNVOwqkM0Taywg2LiPiqa69G2paFtRLMIQZxw==","shasum":"9dd6b9a8fcdd81f3546bb566333d3d7db26aaf36","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.21.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQa2fCRA9TVsSAnZWagAAvegP/jVYpcRTHfKlisjxvDzq\npjiAEBENtVeCPRodfHlmvTjC0UFXMMi7A3g4rSMGmQqAvpXXM/x3LYoIzKfH\nneo5UaYY2r8JGGo+2o8/Q4ppVOJ7F19ruTLkp/+iGODZE16FwteBXdSAdpwi\npJ/NDHRaChtQjBLQPIFgTR2hl6xNUMgeVPGIpb1f+ggYgD90eyx4pz1rOno+\nt29Mi5q+FkQyBMeBxPVoumZwFwUxApJwD35BA2zJ5b7GUPQUD0eU5djuy0SV\nEOW/zWn9eMMnppAcdWKrP80BRGqi5pxySvEs82Ij6AKWAiEdWMkd5iKXNtp6\nukmqJdwGnTd1BqbXF0CeIzoiNJHeRenJDaHjY9ROST2q7M+uHTiQFPuMkw9f\n3z9AlugvNmOpqnxPt1AmcAGG8t8DaHWs9uTo1OBqpKCbmCltloF9qwuxtucH\npkS+t/XFV6fIcax9PVb6+UyFwWpNpSbsIn7njacXKqf5iMdv1aw5bTCdh6xG\nrEAGCBf/fst3llnpkbjMsmIWVpONOEBuLRrzMoky9GYNBNoQ2cFDCcX6wuxv\nZeXlpdluriSzEzbiYAbciWD/ZW+YH6sVi2d8kIy6QldH9zagAuSSTwRBrif3\n5jaGCN19exPim3DuTc2iRtU6e2k2jg26YvQAh4x9CE9GVI9YGmLT1dC7buV6\nvj3+\r\n=gYKF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA2tu7HizBTbnlyJblpwY2pscnLxAlfQaxXXyB+Nd59BAiBsfQ5DOn0B8nc0nY5/6wmfjjhYr8WRWBAAHTwtVSKOrQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.21_1581362591557_0.35876029175024393"},"_hasShrinkwrap":false},"1.0.0-rc1.22":{"name":"plumier","version":"1.0.0-rc1.22","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.22+e2d267b","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e2d267bf20360c9b3d4638a440a731161f993c3f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.22","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-JQiO/IVVHeIsqGtGbIsKHi2uDjJGSZOyykCYl45NHgRSyaZH4y/GZAi23xr6GCRUAegWObrQBuO/Xuo0DvLQeQ==","shasum":"2576c04d397bca6ca97cb7bfb543c73c043ab892","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.22.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQbMdCRA9TVsSAnZWagAAjJIP/RUfwoTDAMPoCUlddbNg\nEUmJtQtIQ6AWdEEDbYyolTPjWgAbp/J5pDbJXyQuAaebkeGBAdlVYMK7Kh6V\njkrUodj6u5a09rz+9Z9zAKPzcpw4gYb0YdFESlrsp9gLwD0/1q5K7pceiEd5\nvzpDVIwujNtKRyGbEOBNIQ7v6GGwRb9ML7PhuviprfYPEHFXoJEmkEVe/Dz4\nEHIdZwMKzXImx/15xpz8o8k7mYfaBBi9kC6fRpDIYEtt/beDxgNF5+1xutRe\n3m4lAmIoktVIfGLKfG9/3DiGZ509LH8RK9art4Mr+jRH57UHMaalk57R7ju7\nsrWLOblBYMHnecV5qYplLH+y0m3vvexbXEN4CO3QtxljW20oqB15l8fNcY/a\nSDv58Yhyd+DERV1dQR+HMJlsHkqExyd+QDCxJQyPDpEcfllKzHZnfpB8waJE\n4fU/Sc2V0DYyIxJYo/ZrgWkxl9/km25ClBEN8HZy0mg+94569XZUUXe52KSS\nMa1vCUkzIDkD+IQOUwlaLmxxI3FsE46Hd5q7vx2Ejj+zo8W48nfM/N0KP4ye\nN75b35RdXtmxzALOqIRTePJvA1NqgbTzngwFMp+gTS9Qsf12Smsw1P0+cxtO\nN3FcujzFxhCBMk3oLPlSqSdn/njC/FI0cc2izQamk0Uv0lidNZK7UKqalIT0\nNWhA\r\n=B1FC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD0F0zoYGcIRoe+cJGfxLlBbYZfi6rNYqjaPByIKhAAmAIgNXbpxIkTmJxTYG+0T9YCAgUlQp8LuOAAKTdMfZ4YoFs="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.22_1581363994580_0.06932825680409715"},"_hasShrinkwrap":false},"1.0.0-rc1.23":{"name":"plumier","version":"1.0.0-rc1.23","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.23+3352825","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3352825e44c98b5b9ad59f230cd2f131f9354a4f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.23","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-+4le4ocYntcK3Zchspthi0shks/ue8R6ePZUwIBJGAp8D8DdoG/VaBbPlvvkgMfQ+dcVVWVJEaGEyEweDVEPxg==","shasum":"c3852e2defd250fb1eac2efaa9a79951c8033e36","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.23.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQbhsCRA9TVsSAnZWagAA05wQAKRre7tBDuP6RVw1vCtV\noTRURWZviT5USaems8MAITpfIQoz0vtPO/2IEyT1GKTLWv9z05gI0xBTcWZH\nu82tV+L+cG12xb8Bi8b9DUwW+SSVJYvPaQ23tX8dWVOw+wrttdrPoZn1R3D4\nGCwRwX3YV0LeEUECiV4xIEpoGfJq10LuGCW20BmVVhnat2PoI9a9ECzVyDBF\nQHJH4VCuWKZxCP2mOX48KQIuDJbOHifUJRJrJnp6aZsPJdrlz4koHh5iG/Jv\nM4lD46YS1Byuftco3H6Z2noFj/fRDJUQqg7WbbN4m1yox+K4GzWrRMC5gvSt\n6ADvh5DrVoDY2s5NCiuaSD/MLx2dUpFqYy7ruHNuEgbgAOPj3wyZ5vuqY3iP\nACa/DeIVjgaWQgIkl3+WDZFVaR/Qg7cflw++OU51Fkd4YA0/AsXNRvEDm28b\nx/G36OkAw2mgBOUnZD182iXEQEeqqepkIcY7yosKunPjfYPCjweeCnU8dRL/\naRtFTJiBB8YYhlMH/54BI6+9lCAuPrLAwIpjFu7Zs6VIqxz+ausfN8Ok7PoY\nWfxvhSyCJChAc3BzEP6taqxJt0jNkbY3dOpPyOVHjQy1AdACqiV3nMU42xaS\n6aaq22xQZ0PZuol8Edk4+NB4erU1BbTQhNrhX8u1riGunAPgnwXo1I7XQMuv\n2eKL\r\n=egn9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbV9TLQOmbPwd+0LFbcFxKnHvQHKkLmJ3ubWy9oIbgHgIhAJm4i3wdT2oIzKDKLZRXYnAYAlGsHlNavFJD+yj5Bib8"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.23_1581365355649_0.7130947338669353"},"_hasShrinkwrap":false},"1.0.0-rc1.24":{"name":"plumier","version":"1.0.0-rc1.24","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.24+9af9fee","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9af9fee1ff2eb32acd222c9a0737aedd839ce6c9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.24","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-dOrXkhj2zfN61HYYvcTMIDLr+0eUsVGZxw3urXS2Wy0r+GGXJMk4JIsg0oDfbD/0ZY8qMvBSGGE9+mjNM1Sw5Q==","shasum":"75ee3ccf0cbab7915b57a963cee9328b59fdf2a3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.24.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQb5UCRA9TVsSAnZWagAAz2AP/ReWmRzsEaMKomLusma7\ndv7MxXelpeuhcfRQ7Nr/ruur8MA7VuT2pXdCNXb+/nOV8eCRw1mLSIme8ocQ\nR5WbSdxA5/BxPBa242/4jkIlYsba70/8d+eho4Gefzhr01kfXt/O5QCkwasv\nPG3uu0zVSs46ZyBiGnEj1lXduDf+RAuSHtZeLXvNr30pTZNpJp14bmgRtueQ\n/xYWqHmkouNwgjnCOrFIeBr51ZfBdgtpU+uMh4NYLscEFITQHYNOAfxGNz7m\noiq0vU3g1RWPReuJR4nqIQ6evG5BWQKZxHhvbLQR7VWJy4DaC/mrpg5j8Ud2\nBje5oeT2loWrgBYpyF291MiAaobZ0SF3EzuOM/A2wYcjvxuHhFLAwX5N2RQ7\nlvozsW1rB6nH2HXPtcZO1fxs9CN9RiQCZDrEmFQHXcu68/6SLfPhussu2fYO\n44DJNUBY9MnVbyKFJZVGEGL8lPwMOl74J5pbIy5uUjDajukhouzTqI+6996M\nbacHJ830ptvYrBMijbO28iRBaXjYxWxDhCgP9uomhU9yvTXTvmndhdlMk8tN\nqgb5/Hyk/ay1KlTJ4Wlc2C7lYp8MDNidB6Sq1rl+k2Ogk+FpUyREldqlHGMQ\nVbAvmYu0VRS/xd6Dvo2T9LsLDgKg/TYu4bKRqyqzAdcX1hWNgud+MK+LYcMe\nNvZl\r\n=c7ga\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC5WL6Nh+D68B9lmdO7Ss7jsEQAc5Spveq7+/6SQc59PwIhAM5lHfwiirFdkdFJzJf1TF7WPSHMmyBPlFSBwbotHqHY"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.24_1581366868475_0.6369050011931745"},"_hasShrinkwrap":false},"1.0.0-rc1.25":{"name":"plumier","version":"1.0.0-rc1.25","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.25+081a5f4","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"081a5f48b8ef159ca53fecd9eaad451cf1373a2f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.25","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-3IeQlAdOPoZmAlWjDyfhccfUfjZRVVK1EG2U1xvNqjA3V1kNR3XM2Zd9QIx30WgFiMMbTc6WKhB4uoKvtDqzJA==","shasum":"080422cc877a80ef07cdf025e7327b5200abb18b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.25.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQgrdCRA9TVsSAnZWagAAJeYP+wf4gi9O2KtVmdt/SsvN\nn6Ghoa522vGH4aBDl7jTaX3M70lYieZoXw+PYQDW5viWVESVc0NA6tzsIzrV\nabPc8QJY8JmAyEtabwMu0iCnoAxGzJyIHJUvArtgDRp2hkgsi4YEOZUSMcO0\nD4ZrRkoIFNig0JUIAm5GfjUzZQxA18CA1afYprnJ6OabIqRGQ3UnJb1HLzWS\nBIsy0K7F8eAP93GM/AbTY8ISx24Xe0hjwOj+FxImlQW+SwJK2mx9NKd62RL4\nctv/lwZPbmJQ+ajc5M5Imt05TRZgVSihzuLbNd++QSHgZtZApLdxKUYMsvmV\nWyulIuCGyBG9nwS8t/lIgGH06Ebe1U37gSAY+k9v+9HD+JvfrN53AfikKAyr\nWHmAa0h+WAgQONg0l/zKJeXrrpU2k5k062flVk3N9OPq7Tc+O2q4g2kLZpDX\n7PD9dgGj20Bk+J04iUYGxqrcVkbnL8ulEyCwuUaJ3afvLKgAxY7JldFOSxxt\nq1eRRsGJQgpeH0npM/adoX9OcnFkD25zlM0HIu7IhW4lbf3IK1uYtfPvIfLq\n/J6R9o2+B9FfvK7X+crUgsVOlaJoFiCF83a1mj5msAFEv2YL1ToPS5TvxCLO\nPq8BoxNl8q1/nvSxm/laVJMGzbyewwCw997mupFw6W8UAUK+d3VF/Gyh1MoP\n+TiF\r\n=NVmw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCAZRVSKfnLa6gYQ7uFRzTgMT/aU3uzev2qoS8zSVV9aAIgLaMvu8ztlk9/RLUa5FzkJbLFjMgC4Yhk7BmWZq8c7E4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.25_1581386460792_0.9626379029782872"},"_hasShrinkwrap":false},"1.0.0-rc1.26":{"name":"plumier","version":"1.0.0-rc1.26","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.26+caa64aa","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"caa64aa48c1842be6ed66c4efa54171da1100b04","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.26","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-5W1FGGxD2jwDAopMdp8OiINW3lbDLkjX54RyNGQvrrq+yKAaj6ke5T8W8N8TJHusrnmcRIN95CNf3DyQkXwCmw==","shasum":"818d06d46ea21d91958931f2aaf80c8602d85439","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.26.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQiGgCRA9TVsSAnZWagAAtwcP/jIkjEcCxOw87TQLckgg\nwp7WRyt/afnV5AqKKlbYtXLlU7oKHsD2B41D3t9GRIrlmNmB6L0Bb9FCqBVM\nNNS+JzRZYrN95yzslsfB2OnkNxWan2k39avhVrH6T3k5rsw4QuvwfqNamoRW\nAlV+z+/KpiXZmJaTuaBMv+YJJlbyWNYnsc+DbWcYXu1I4Nd9edwjDIgyNtI2\nz4k71UBKeKDtzoLrK6J/65wEGoMhGIECOkRXAuVYk+RgUf6QPwWvSZ7F6zP2\nd0b4P+yPAh0MUtZ40fchZ+e7rZggjGq6WpglacHjDZpn7DgFxpeUhcZdGgMD\n5gAYKAGS/J2Wi381U/xfBl6NuoETsyPhvmF93VCSXgK5AVQBn4o6UODKGGxB\nMjJ2zU8t+9dt2HgGWXoSy7FEkLW9XDmXi919uPYFEr5R5xBmsbAhEeU782cs\nu/WPGJD0s7krD5Im7ggjDZOl1U4xYj2YArwp7M2F+ONYjWRFD7G0ACMWvefB\nQK1P+pKkmOqz47l+ZHCPysreIdy0+mT59HrVvO5yiTx3JP3Wt5pvE9cr5IBf\ncdQooAdomXzYv65byvdfl9Ht60f/OaQrvX8tvTcXoAplojhzVd/EfEPDzWha\nkcq3MWQxhUHPIhwprok1W6ejedyWO0WKpRreo85SzL06F+RMAcYeKPB1vMsd\nX3Aj\r\n=wf+k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDU104RzoP4xyyzdZxKfg7064uZAlHfNBOJklEkE9sqEQIgbg0ht2b8SbUL8ZqXX2G9qdIlAvjOj1XQVV30503JG3s="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.26_1581392287999_0.045001253507731764"},"_hasShrinkwrap":false},"1.0.0-rc1.27":{"name":"plumier","version":"1.0.0-rc1.27","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.27+d4a0ad8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d4a0ad8953aad82a851c93b19af5b0d827bce7e9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.27","_nodeVersion":"12.15.0","_npmVersion":"lerna/3.20.2/node@v12.15.0+x64 (linux)","dist":{"integrity":"sha512-Cbx52xCacURJphYYAi1JdHnSw0/b0c1L0o610eU/T2RTu2RKZcpnsFbOrnckFi9X0TjxpiJucUHdRJ9RgbYWbg==","shasum":"26bc14ebeeec9da80e45539d93840884fe4b60f1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.27.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQmIQCRA9TVsSAnZWagAAItUQAJg6m6DCCA4+VexqjT+x\nD9WOJO1WAn1r6YQWrefZv+KKsgi2H48tUbwXc6mw3DpVZGJnQfCLaqoceUbQ\nWEK/s3Y+Da6JCKR8FNR8Gk1vrHR5uqToGo9M+e7PsZ7E6ikQxBAO62cdl6MK\nL/5xOrkZKyTDqOCBBIJ8BUnJFExGgsCbezTyJjgDhwL04DOoiLaY/4dUUuPa\ngE2nB46V1DW+6zjO0J2nEIyL6QhvgujxAerX2c9sGLl5SVyREn0Kn4j6iLTT\n3IAxMyPCJkfbIgrFhB9dxmGhMD7kK/So7eaDPIpVogkQnOUh/h8f5MJvOv2H\nnoadcHub+6mMVluBIKyRb8pyyeUwxZAIjw3ZjpDQb7BSVhuoXKjNGiBSE7eP\nwd5UDFGT/IzlT2kqd/hlAl3REcAnLeAWaNBXZ55ZHKXSEkyjFJ//uDBuAlZg\nMP8ma9DVM6mlskqVfgU8sQl0Cq2HV0kqCCoypetdttTrS0REeY686PjSYsw5\neArbFPdwsiVlmb7Z0XJHpooofyCGmLk8ik2oVB9MtQVymtn6RY3tuh65thG7\ntzkNzLSVqPNV4myitQXNTlW5KbBP4y7KAzvo/PJJcgfL7O5qcdHCrsEa1D5x\nk9UINWNpERxdh18M4LQR7/PNEOdrHabEXkGARZlSWFTYRqXG5i/UbtklSjrN\nHpS3\r\n=5H3P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/2TkWNoEK5bEmfRTK9GLMUGBJeaL+jEFdfaQTyS5wHgIgbs818gDZ5YnUjiFolwf3ApDvWbAjl17jgm1Q7iZuIL4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.27_1581408784083_0.9563886233359424"},"_hasShrinkwrap":false},"1.0.0-rc1.28":{"name":"plumier","version":"1.0.0-rc1.28","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.28+1f4053e","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1f4053ef30a10455c262461c11fe9453da0ab23c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.28","_nodeVersion":"12.16.0","_npmVersion":"lerna/3.20.2/node@v12.16.0+x64 (linux)","dist":{"integrity":"sha512-7gqxYiLmJZ+ZQKMsMZ8fkzJz5SecGcJ/TB2hXjp6WoJ5l8F5auqa3/uCLLNQL8ifxKsby8CCqTO2QUvERotHVQ==","shasum":"da9c46bf83cec1f671c8283d9c4cbd9fa833582d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.28.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRHk3CRA9TVsSAnZWagAAjmQP/3AItVP0bJVwOA1C0x1x\nQQbnp1w7ac+Ey1Sc9ZlkExJ8MYZdUxe8df0ZLKy+1IISc9S99wsvHJfjmBE5\n7q1iJLnE7kVhlZopSsXeO5pSJG88MlJYaE5Kzn+0aFTTinBzhU39R3sAMSDB\nj9oiwiEnSyFac0l2kDP5rkfqXyP5nkQZha9al5H1m01DC4Pb3X2uL7Ct39W6\nTRdF+hnJ1yG8oRHlnpJsiIvGLFb7It/vr9CIpHSQfvf00mDLvjijLw4FLrXS\nF5vZYcfdqS16kgwdXoCdPXmMA/7K+u3BxTUE1cUTaM1hFqBoXOgKOldN3L3d\nR2NXRTQPF8pD6+z8J6//LH1JPG//0pwB77k2xCHQCYY4/pQOVLeEjhym1Dj0\nqpkrRW2QMmdI6hakjjf1UAurOqYPw0pBqTBOX6RoUSanTXVWhIuXvyGmszCB\nSn5JVv5FRUqQaVjZBnx3+nAUDPnez2S/tS1NojgbSRtDMuB8ISamiczyqFOi\nkAUiGIU5ljSylYEFZZmKJ7D88W5Y9FbgVDWMOzZbayjKsw87mRpTUBG4Ey3X\nWCnOBk5UdURiuaS+nTPUSyLQBkd2ebtTCH/lRdSNwjNQhyOF/ee8ILPPsoNj\nY232PtN3xBhYY+kA/ejSk5e9Zw7yJPIB1a3oVmAgvBpWHt11w6xJzLfrRNJC\n6U8n\r\n=FbWG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDb1ZTPrmIdUTourUwpcFEG3UZECwYBmUFmcUyIMKLbmQIgNTSAhZVDZ90j66vZ5otPtE6tCrSH1LBtNVrAqDguK1g="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.28_1581545782721_0.12043048168319048"},"_hasShrinkwrap":false},"1.0.0-rc1.29":{"name":"plumier","version":"1.0.0-rc1.29","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.29+a482d91","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a482d913c592d6f9ad0204e690c629c41653c270","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.29","_nodeVersion":"12.16.0","_npmVersion":"lerna/3.20.2/node@v12.16.0+x64 (linux)","dist":{"integrity":"sha512-f2Z2A6pihaMQQqNiz/YxxQpWn8JElJqYLVYmwqZ/ijIrQ8ukmN8Vit3fzGApcQvIejVHc5dq32D1CpO+QoeKgw==","shasum":"cd3a799a3a696d3f2f0990203ee3786b44e222ca","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.29.tgz","fileCount":11,"unpackedSize":23160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRJEWCRA9TVsSAnZWagAALlQP/1J2R3vc3LyCfERZ471q\nro6pdfwOStx0kxfVX9cVDa53crldf0iMYdGCxUlWGS8F9Ho3lehjl1Mm11kb\nj8VhnbHGBTJXUKBzElzJ5wt/iu2PwTnA4cafgFvWiyQIyHpOvdZpAIf47lnd\n9T5tj0L1MMJj0lU41qlsk1YG/n8kOhAYrlCP+0uBe4JulDeIaB3sTFtIBs0I\n4oiqJ3OF0v+4csg+oh0hvUlECNIjfKuqEr48EOgKMdVQ03OZPKg5Bh/aKDB0\nPGYx3g+v/sIMzhOsaJzkrzbQN/aoBtRbeHjnvH36o0v/0vPPNaflnGHl95Bu\nnt37kVVUYAMQmlazC9bsdbpPIt38A2qm04AC+1XKPiTZrzPZfHPY76vcYDh7\nTuEdHEiBTK6SbBJ6nJ7l3Cf4KhxgcAuaNYsJm21WXm++Y1bwjH9cyFa65JjY\n5oICFGJHv8tLrEdbfTmVSYxGvZXYGrqhoqy6et5TzIkUedxV+xSFIMnmp5rB\npQijXPfCT8g2IByqRILFKY7FjLimekxitF6KtoUNaTU86w/NaOh/h+f8crAW\nh8hMweQaG+xd+E6FSr++DeYeD0PmFlxBfIcFTjAAQhAIiKR3g9nyTlJ+4K9c\nIFgVXol9mDtXIGaeCPEwaUj4pTLvLixAtQwTpXtZSNtovevi2JkbK7wG97P3\nkE2L\r\n=k3sf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD0nA/mPWXrIPzEl9pmUgQfcpsb+/JA7yssrFKOsWLyWAIhAJShFFlfXFWebQBDbjG82ZF8CpuDa84m02W2i6tvsv8T"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.29_1581551893993_0.12986745568689417"},"_hasShrinkwrap":false},"1.0.0-rc1.30":{"name":"plumier","version":"1.0.0-rc1.30","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.30+30e9726","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"30e97260a3b02b9450f7352a08cce0eb4f09a67c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.30","_nodeVersion":"12.16.0","_npmVersion":"lerna/3.20.2/node@v12.16.0+x64 (linux)","dist":{"integrity":"sha512-4QTlulnfMAqf8V0vxovs//Y5S6JJdWba6UGMOuWHCZTA0bCOvCAtadSwOQZY8e2Q8DXBCQsfMmTi4LK/zobKJQ==","shasum":"f239f32d7d48ab573fc948e30321a34a1dd39d5e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.30.tgz","fileCount":11,"unpackedSize":23569,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRP4xCRA9TVsSAnZWagAA+uYQAJ28hAfp7Gigb7SJwlLW\nXN/ZjXTlkYbVfJ/CnJZqgKG/ZcbUJtBkfimGEd8WwRto+ho3tgNoYLRWuM/B\nVgDxzChVzAU6DvaAOD3iLsJAoPcYrqJ5GMcBJFA0A41Sd31uvAjpi4nnlQWR\nJY4MMKj62s/9asqHuH1iFRFyX/mCmlvbJu2Hbi+9kXVvmBAqBwSKGdGgSkF9\nD44zNxHIQrIDoKlt8hj7pJIdNnCEz/v0cM8sFPdx+KbsUFLAWEc4ZyBG89mC\n6lU6FiUxv5J7Y98MnT5HZfAQ/u1aT/zRIjtFX3WyO2uyuiqKC37nR9EJlLkP\nD9zb5kVOWGu3fpA/4wDF6reEI6q6wnOs37LwTmOYEnDSPzOKNzqlAX7WXKdA\nJqixSGnZx1pQOlJigJlUN5zadHM7+Vbg551c2xAYtN4nFlT3oDzeRQS/AzUz\nKR3hAMIKEGkDOY4bK/7Gy4m9UyJjSy67qQomt7Wilf9elwL+MpllWnfbkIiL\n5bXAW37H9/ldw6zTGocTnQsJZlaf2qSEV4plLuh+HQqewtrlkWVLboGiscNh\ntgyIGJlprzhQ3hbyQIeL88X8uKjQaz8lyyDynGWheXim/+B3OmO6C96v3SH5\nLoiANEwneXvF5fQ1YRvU7QQuH8AgDbO30BuTDS3vBUj8cO0MTi9up6tQG3yb\nWBga\r\n=xEtG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCtPktuFmTol+AryBQ7iXgzpCd89eHl1Mlz3rxfnIzYzwIhAMqeShFoqfu61BwLGxx7X97Yki8S8JJO/FJuGIUSTmQt"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.30_1581579825336_0.6494805886533397"},"_hasShrinkwrap":false},"1.0.0-rc1.31":{"name":"plumier","version":"1.0.0-rc1.31","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.31+a25be92","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a25be92fedf71879a6a9c88c5850ea047282e30a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.31","_nodeVersion":"12.16.0","_npmVersion":"lerna/3.20.2/node@v12.16.0+x64 (linux)","dist":{"integrity":"sha512-X4QWmmCO8TMgYXMD01K7QwyNX+IhdeKbs3mEDxCn/Fwh+8CG1XgaXDJB4HXadKlQTO/w8GryUUG7PSWGQkpR9Q==","shasum":"9e841f339e22d4e3a7ec9e09a5d9d0abf4421fae","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.31.tgz","fileCount":11,"unpackedSize":23806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRdxTCRA9TVsSAnZWagAArXkP/iq+ORoUmtl+dsTyf13h\nzEINvscej6LO8b4lnTCqJu5TYAOnw3KdVHUkdeEqStZMBvxgtk31bVj2Xgtm\nHP7HYVp+mtR0vq+H+5gQCLIrMrW2OQfB8yj0miD5ci2vfhHMrZopv5i/gmKG\njtajlvk0cdu7TJiVelcv1bhLZgkZMLb9p7e65FPc1yKFVJs24f/n96DNv74H\naXyMjUR8yi9RtHRVgpIKJMOGt6Jo9ny62oQgn6ARTnATw9noqkFlGqrn2vJK\ns0rl9eYYtcj2uszcNT7gctoiRIWc+B9EA/xHbfhziDI6ol02M62OBrFpiiRN\njN21LY2mHdqs/h9/J9jm7LFx8Q/Sd/tbBaRVomPslB3yQm6EKp2p16p3FRld\n1cS3A6XOAHmZRDFFD1qHHLBR5iJPalvNgVfQVpOu9py9nrLFdGFGpCDWyF8B\nbKnPOmvc0GxtRBDS8vaLvfJmYZ0RpeeKMBIVg06hnGyzQV8QAEl1XqfOAZbj\nk+kWqZAA1zvF5JkTR+aQOewdgdywS7ynfw0ehZwANCG/N3Xu4pJFLSecJXUP\nl/fS1Uy/RM+yhJPSQMLz6FT8/Jqxshor6eq0IOTuIdlZm916of9chfLczPVS\n7oLs3hiP/orRskKLmgjKxtcKH6V4pfC3ORbWklNrrqHZ1LHTzCcPFy8TanhK\nQKAs\r\n=LL5x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC4rKX65T7pIko+ypL2JAlyRSeTbEsqC0Yg5MHFBT0YPAIhALHVfM+ZGpBtZxZh/JqLsYHmvOJTjn9oUbr5zpKtObUc"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.31_1581636690678_0.48564780726233536"},"_hasShrinkwrap":false},"1.0.0-rc1.32":{"name":"plumier","version":"1.0.0-rc1.32","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.32+f4bba8f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f4bba8f7e55be82f419a2ce9b9d793166d94b0d1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.32","_nodeVersion":"12.16.0","_npmVersion":"lerna/3.20.2/node@v12.16.0+x64 (linux)","dist":{"integrity":"sha512-4xqi9D8IKrXgibXELVQv5aowVNoUTKDh8YfFoWNsE7FX3N+Tz5FHNqKiONuSRLf4ZioFJ4uQNvRn0+D07wBKXA==","shasum":"315a03c97a0f234372dc856cdb2e3778e9cea81e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.32.tgz","fileCount":11,"unpackedSize":24039,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRpu8CRA9TVsSAnZWagAAAFwP/16IxQQPpeDXDnZj7nTV\n/5/wmi8/qXt6O6iIzSO92mkguxYHePHutuw927TMOe3OT3jPzJCUymYBt+1j\nGZ29XCQN/10/1fbR58GHrfLHlAqAUZzZazBcSYxoTUwsqiyPsOzsQ+EtbeMo\nAqSiwNiHiEkZfakGd1bSAlUkajdsRIgfNXwfunX5dRFaslm0/124TCvM+HD5\n0zyqXI5gjCPavS8Lf3IK6lB63J0Bl1Yq6zq6rBy6PTARuzVfaRuNuYBL5azA\n/r8CgkvrZ0qxVwYLv9fTXPKh+/OR0fBY9O1DwctNoS3+0JxFV+nQIl2q8mO7\nMfG6FnijxsnEwOunyacZ0PGv0gCXNwc+nB3JOIRpsbap7AYszsqPAszHppCX\nKugSZv9OuPF5nRpVE4zkTco6tUauvd/e8Fun30QJibFEHT6julD8uMqQ7rw+\nWLfkmZ4X73MLys3kv3Np3Mm88Q1yDN5V8x73K231ikQrD48UmJEHcLtoCjt0\nLPewX3Vr5wak01cAGUPvopi24p/f7W4qfjy1SYAUGGytYUvhU12s2F+bz1YV\nftH/fey8g5egczJYTMtKKoehCWtGcfZZuzjzrpjjECB0TDYfWKW5Pj4J3hsy\nE3qfsQi+Dv7jH5Q5HgN5YwdYBIcJSPccz2wRBA9IJG138cFO0IJhMlHWHP3U\nJg+u\r\n=kKJ6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHkfED67IgfRvMzCodhp8Je+9xZVWvbFDrrxJn7HN7WAAiEAvNFU6z+ARiy2X/IXY0iXGMTsGTRkS6VCsRKRCjBFj/M="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.32_1581685692236_0.3998225366415884"},"_hasShrinkwrap":false},"1.0.0-rc1.33":{"name":"plumier","version":"1.0.0-rc1.33","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.33+aa17255","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"aa17255f7243ad8169a1081f3beb276ba364d87d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.33","_nodeVersion":"12.16.0","_npmVersion":"lerna/3.20.2/node@v12.16.0+x64 (linux)","dist":{"integrity":"sha512-TJbCp6hk4/FI7J/nm3S4ozdHKgixcumVkkJOx1UACWk6pYDVoiJwPsfZSDVSefshL6Lra4a5JrzgCZoKDFVwLA==","shasum":"9cab67128cc67a98244d2a1cc6a42fa6541582dd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.33.tgz","fileCount":11,"unpackedSize":24483,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRycoCRA9TVsSAnZWagAAZmAQAIcLHXSDFyTW3d7o4z2o\npmTJp2wmhevDzS6g0C9GHx75pfiDM0DTIJyvLCoY5+5Cj+KJecquF+kfkcUv\nOJuCReLecQb4zciZJZJcMT/URYFr2EnMyAzg6M/8c7Rz/56Uoxd0TirdWKYs\nTAGOfyRsGoSqNGXawCoUh2bjwZ9vV1EWfUQcSKJcZ2OHKZHE90nhqt5mJyiD\nSGtVQr//CYkLyv9G49GqxcA9xhH3pJhRFzMbeMm2btVu50eHs4F47exU9Rsk\nF8+TCBtVHlJavzt+bkWVrNAxEkwnShSJkKy7O8bch2L8TU/9/L6DMPeyq+JF\nChTjNRsVzA+JMMOJEQz/YxvmBwdjQVnMlZxuE17uBZ96nkicq66j8zOaLpQU\nViwUPdRe+FN7thcKNNaGQfR2l2aSmnmn/VjJP67HfmOosTJ24BRtW2e5gMfD\nrK4nQt35GVeMdNMvB9LJII+d+DJkU2//sueXcJNOZS1DlSKMtBQQOnDo1sn7\nf20c5LMqAPDvPRmv73xHLYqL+rzwNAT7esMzpfqCxCEXD8kMl+bM1kDO+RUL\nPnuA2lE0KZrvfay6mXbBfLlDkFza6zjGsqeJrhpI05VTcAyPcG1Gi6iuTMEC\nECqvJgiMcfzlHvKzjEZ4Y7et7U4wKNYJ/gJwNvId68AHWgxgKIklvkRHd/cs\nWGx3\r\n=hopY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDhWXDoouzj30Zg7vEifustSEajbJ6ULRi3n/Ll+iNXmAiEArIPx5xTR8EUGKiBE0nsMEx3NnuovdTGZMaiRoNymTAg="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.33_1581721383812_0.7023066438870653"},"_hasShrinkwrap":false},"1.0.0-rc1.34":{"name":"plumier","version":"1.0.0-rc1.34","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.34+a6fc519","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a6fc519f04989f7b122478e6569944f094702c65","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.34","_nodeVersion":"12.16.0","_npmVersion":"lerna/3.20.2/node@v12.16.0+x64 (linux)","dist":{"integrity":"sha512-hDZVsDsoRQLZAJqPky6q9MS+AAYh4oYElEKamYUxxw2bYwo+VBPO6JUjgjczofvi/02RwSCsmCLuKOIz9g4D8w==","shasum":"64a5ad7ac0b4154e3b6492c5c4545616b7d21681","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.34.tgz","fileCount":11,"unpackedSize":24842,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRzZjCRA9TVsSAnZWagAAK9cQAIadPxZssSiLC/U1R71X\nkvKCHGrCrb1Q7QiTth3+d4AjQVOsRCCR9KO0BeFRxArGPoLlYNRl7PRm/990\nzD1h4+ibIIEdt625tt9rPTRjTFDQC7nKbHZ1CUbLTG4GwQjEcijkiEruHdrJ\nmT1YacznYnCIPpB5+v29LLu+2PLBOvfjk9+WK7LKJGwRq1TXrxWmvicUOu9r\nvXW8mEt91qmQLfJ6wJJdZLy67NPJnz91TH4dVjEwkFVEAg+NenyAd5Z5z/TU\n6IQcrZy+fG0ktThCBg18dYGL/kMlCIKOou7po1OXN68KCPLdBNZlejSF6dX2\nwhD1LQcpBSZ0QnscsH9MDUBZmdZV6yMyQEUSxOrTWyVC9T+X+35PsMtFmJqY\nOKa5MCg1NM5Z7Aidz4E5mUlOlZu7YlwjDTUPAnrx9KgON5kjmBIW6vBkcyXp\nNVb9luIejWrmqok4G17B75eYVJA1Dz/P+bqkajmbSVrwEZhKEyAyAgafUTlj\ng8MPnEuSOZFpMD2vM5iizJ/u0lvELRcmnqRDT3FmrYztBc3o9AvGAL6z6wBK\n+KYig379GfTqcQkQ4k6yJOJp21Hb5YJ9q/UlnH+JvEgbvowc+wnAlmLh3Ne9\niilG5+0/nUVi5tMCgti1UF3/vvOLO0L558kFQ9T4T+VDgdvndGt984We5JnD\nwQ50\r\n=+/MS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC27lo1BSDmy/BBM7OYyihKdZFAwVl7Y5UNXirL4dwXFgIhAOHZZKevcFjkLV114GRVApCn0Xwa3o+hFtG4EPORux2h"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.34_1581725282870_0.3059238080109481"},"_hasShrinkwrap":false},"1.0.0-rc1.35":{"name":"plumier","version":"1.0.0-rc1.35","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.35+f0f756b","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f0f756bb588c42ec76b7bd4d1f7283db7116304b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.35","_nodeVersion":"12.16.0","_npmVersion":"lerna/3.20.2/node@v12.16.0+x64 (linux)","dist":{"integrity":"sha512-gJHu7GlwFa1M3JHMkLWDmZu6+wfXTITxnvNAzmQ6RLHmCzIBLsvFHC5I8ke189RIn7ZeAjyivUEseJklEE4iMQ==","shasum":"e8ef2f76dc5418dd0a48327da9536d0f55cd7d8c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.35.tgz","fileCount":11,"unpackedSize":24929,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeR0BzCRA9TVsSAnZWagAAA3cP/2SJrUHD3STGfUDrhsde\n73puezRFRC7hy2FxnNI9VO4AwITIjVrjwkFYo5s2Muj3o7CSnOvHBaYG3zwt\nAOipj77EnUd4Xsqe7Yvys6pf1O8dDvQtdsXEDJ5HrXSIhLLat8EDdHYRLhxH\nSWuiP89qFlUPzxmMOsN8H7bXc8eDNtJjRKY64ftNRHNffUKMevusyUR6Hm6N\nCmFFxf6M+vaA6jLFg5R6lnaG32U7EYVc7xgTfSp687+dY9+XuLTZM259PlMo\nGSGjBpTTpws505w4Kwz26KkCzafeIJQe02rItzphZRfCxWsi7VvpOZq7piN9\nnfiDcguqTli6H3piS1jgagQo4FK51kI9pq+W3QUOBAxpd+4QmlNRdyM9pXji\niMBEafig2+JZgoMMwzzX3gjjwyc4iI/gwlgA1UpyT260P6jc9VhL+NZUPPwa\nCK/qoC0wRYdlC9FISdDVjmjYBZS7TXyhvBIdwwdvOj8e0wXPJjTFzVrhMFGa\nQ7c/qnK95CRkHM2Fi7k8iT8o0mperiBLFy3oYXThdmso9XJSw9CdpE8HjfcK\nD2FIl1oFwMv4FejoC6f87OXHI3ZQDxF06081toOrCaB51UUdADUSRjsm6++7\nTyOzl9hMWkHeK87mnlJwjXPca4OrQm+Sq9e1iWQRqiASLHrADDm0prV23vay\n+3Xf\r\n=PQMB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIArXtEKKeVIstBaQTj8aMl3C/8VGq304MyqFrI4hR4+fAiEA2PBbmG4E2JL693/8tTpjs7ehUKxRf/aCaUhPj0IQE3c="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.35_1581727858568_0.4675518517515562"},"_hasShrinkwrap":false},"1.0.0-rc1.36":{"name":"plumier","version":"1.0.0-rc1.36","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.36+6707419","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"67074192c4a2af9814a0c175511026de416ac99c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.36","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-hlM1l6FD6gT++15JNIuVMDzs+5R46y/fO4deLQLnYubrIgt9kVAE5vc33HM1A+rrOb8pXIdU/js6RzThZnabaQ==","shasum":"27a9c9dd5e38183ea934e186aa00527d1739cafe","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.36.tgz","fileCount":13,"unpackedSize":25730,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeUHszCRA9TVsSAnZWagAA8u8P/0WrqQivc2gEMbQEM8rb\n9hOHmvSdXoY8WnpmQ+Ju+4q7XB4YthcP+SlzllQBmOAg7rElg3rtupKUm2Or\n5fwtMAWg0OC1smHjL9szhsyGL0A9Ex6LB4NO6WhbUmNihnDIeDhzc68KO9Oj\n40bcXQmE6eIt8D94uxJyCt/zLGYACQLOYaCBFtzgsYhF1amoufXw9hB8R2NI\nfzoy9mhYuH4yIAdOPkL1FN6d8sgRGU7zg0l7/urv8Eb6MnHcglsK2c3vaZpk\n+NkncdFbKXNyF/371M0Im/gKL0PIojZvXDsRoGdKkmGaQrr1fe+5RLupFQsO\nEkiYIuza5nWH+Klox0KSJKw8q9K3dYDLO4gZvVro1sD4mCjbqrFnVqRbXFrj\neeVT8UwZ+6mLbavwbRiy+BZUk9mYDF8qxtIXG61P7dtV/u5gKVbQ5um2HFa7\nR64QTQs7de43SAxLq/rMbSOLNrJ6moDUE6rgYKMHUCg2lmSDf62uDAGkykQg\nXq+1yo4Pl3VJ5j+GUiNevCM54Ss9H+CF1qoHLj/xQPhYz3pHakL37YoSbmce\nBWc+G2MaF1J7HQZ673Iek4ujSySSuXx55hqvlIjMkFfl8cNrgFrZVxbTtAh8\nhjCa1RTt2ecVIbJHfExHcuBldD+aKDcaN/g9Ltx0AbBAg3QYJiXwm7KVdvxW\nGfMz\r\n=A7+c\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID1cD5DVFJxbruCUrJQ6PFRysu0hFxBNOVauyfyOKQ3qAiADhky51onpnshArnItTOn69hBsTV3DQl7b568wZhyi9Q=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.36_1582332723267_0.3049176623570353"},"_hasShrinkwrap":false},"1.0.0-rc1.37":{"name":"plumier","version":"1.0.0-rc1.37","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.37+a0ea267","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a0ea2671e6665e643d11cde873afcaa35f75cd39","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.37","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-pSh1SOEIrna4pey08f6MudJDVtFSj8vU1vGkBMPRemKlwA07qCeEFSzroht/+c76iBFfMfia2o8OQ0zybTI09A==","shasum":"5e416da1569e5951bee9d79b466d21e0699286db","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.37.tgz","fileCount":13,"unpackedSize":25613,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeUMjECRA9TVsSAnZWagAA1yYP/AocTbtVvmtXOtMe/yS9\n9SxFodXO1i+34rZP9iK8W0etJliBwrCDJ35DzxM3canw0/ynfIZLX56Rbn0x\nrA5V77XZH80qRtyjtYBLJmqsUrXrW4nJ2wren/FYkY6u0g+LyN5s5q2YQsIe\n3dhmx5ZNt2CoMxxn2ZevqbrbXdv468sEoF5CQXBzUzwn5EqG1352r2eoIcqe\nCBdnp0O/HkY/iN7BEymcHCwAGv76fKFki2truiXa9VBsDECm+wSCnKJZpPdq\nAcLZM4gjorbYGR1nMCO5AKZExYqgm6wW5YVUqjY7cqNCPvhCpYbxOnq8WBdq\nDiBtopSGbmJgSz3BQTcbwMEnJ58oqQYZ0v/00AFsHWawJULvjRofXU44a1sU\nYE3NTPKZqAPmYBBpV24PuWl9zhNeatWP6w4/wEymXVImuxBXNLz91QK+VcSL\nD5tVmyUbI/LR6liLF5/fpmWveGPPZeJKyAIwlmUU5QtVZHz0fMXXUw7DK3HK\nJkujVQWPvxB+03rd4gDwwjdKQMEfpXVvnUq6XYOcz3zRnTALnvANikOCHAhS\n4S597nMJItSQPapkdNvJjl0VuoDfgy8/oE38DThBNaiJgSjfKCi1CILDW+N8\noaowr2rnlNj0ToNSKpnJgjealujd5sjgKS1OCB1NYNhAwVXm/zL8mQAGwPEL\nEO3s\r\n=GgNQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCeMvo6RnkJvFizJ2enP+LJ2qL01/Ip+zfLXSK4kik17QIgPPy/qeZSHmSdWaI5B4trC1kZdkcmTnQQHE5brRJu2J8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.37_1582352580245_0.8449543097691081"},"_hasShrinkwrap":false},"1.0.0-rc1.38":{"name":"plumier","version":"1.0.0-rc1.38","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.38+e9fd8e0","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e9fd8e002bfab332ab287047ffb8b6950a053587","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.38","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-xJnJHokMVSJmc72fM24pAkSrxmzKUMkN0MmV9rt+LbbvjKZ97J2F2XcNNyL2BN8iwrRrccVifXAWTBaqvIKVBw==","shasum":"7b2bd0c3eea01c9d9cac5ef78116a2766c193b93","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.38.tgz","fileCount":13,"unpackedSize":25729,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeW0w7CRA9TVsSAnZWagAATTcP+wU7RBkxOfMv+UYT5NY/\nRsmSUWb/b61QrjeA6sbHMmm+1tCXsFbUJ1x2feBZMfdVhljq/G8SqIRGulOA\n7cju3ZOvXfvl4y5c6EvIzMuwS7S3ylVWhI8F2AWtjyzy9glmXY0ulQoxIBdI\nMaKdfL+aVDy4nDKUznSU7VSvDwepv58EXsWElp27CkPrvvino7Wy4L79TaNh\nPH697HpTL8Jd1fNDL1I/+QKWGifF8qSNF0fQxWRCLv3xfAK4mXi5UrSn18/X\nS7IYj/jsTZDNeRqNO0UDcJF/7BbM2RChkWHWcqvsvqPqceZ0KlVri9TOILQu\nx3t0imtJx2hyky8XjTgXx1cdhjMHncqpfEMKhC41Ls2gqnBarFA/dJeANtSM\nYSjitH7/AlSlN2GYtv7yjUiv1LbTt+rEocZtyJxhMV/KMNYBTKiTExqp31Q7\nQMeoZ6lPBPzH7m6rhldjp1PJSDv1NIx41ksZ75WuWDUcb6/KfWHr0sD1PCm1\nJJO7TqDIX6QZrM0trt9O6ANSXEUBLP1ZSuAzpIItmyBUbQ8JCu/4tlU0Sdtv\nFMG8Fv0rrGtVkFOR7XQzVuu+0Ouez1yM7Kt5hOBREx6zKXN0nK4O16HGtbFj\nwkWUmdOzh9V4G0AXUKllYpQj05+zi1hZYTtRm5qLEQXnI9I7x+fNLst4Twcn\nVmmG\r\n=ntO7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDuC929WFRI5RRRuH4ng9umdt652eEgIh36uyRlJEQIegIgNiF8a8oe636f+2HbLnowd4GNBl0JD5oXXNMBvMZD/fA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.38_1583041595233_0.22635392781856623"},"_hasShrinkwrap":false},"1.0.0-rc1.40":{"name":"plumier","version":"1.0.0-rc1.40","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.40+c4e4dc3","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c4e4dc3b15a94219d5b28dd592630b677e84830f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.40","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-REa97iV8mFlxQGaSoRGDTtZIgFuiyK8xPyuz/soDG0LJJdbrEKTdcaqrmXnzKckZngxtY6GCgNgaAzTaMhX0aQ==","shasum":"47a0813e45ea7eb54fda02ed027480c916e2ae4f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.40.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeW2sKCRA9TVsSAnZWagAAxt0P/1YBckp+BpZA4lzLyUK2\nIE3MfSu6sTz1Frlpa0rbFwK6SzGdwLB54/sma+nEM5Ny9NP91fH1S6BVpGSM\ni8UsaQsgP7TZ6JtohaJ5SF4gn7JHLLjvACJzyfPvJ9CaMHCAGDJ76GthtPL4\nSeP3ETSDpzaGrUwY7zCYFZBbaaFE35COsmu4m8Q6kzthA1ec+xZSETXfgGVK\nPJWVhDLxWdTsXAltWZpptBJ5BzZe7f9WJN3ki4pOwAyEonchmzciMwh1N9qW\nIwlDRTp3ZVcbRcg5Uu3Alp0mz8EcH461pn+eNXxg7Tu0dCeW7bWUmoammaHf\n8qs/KJP6OJqmY22/gQMWuQ8070dC025tXiWG2fExXHGtShFF/wSRJzXh/h6l\nKU6JidZXAwmS5V7XmhJR8wgESU0PYBA00Q4V+hxkilrYeLr8o0+FGmXW1jhm\nwnMAhykDVzGIIEin0pBlGMn9hrnJHVY/oGQFsDm5ermELIKPHZ08vigw9oNs\nlh9IbbjJIJiYPU43N5LkLsGMNpw10dvVMglDTIkjSo2tE88se/Pv0KVHLxiZ\n29cTDpJOeaup+LyoeRdplKREQ2za4aUZwBMlVMcJTevcf3gBTF6PkA1aYP70\nL/ZsRbbyDs/Qwd+KtLuxuIvTzCsR2isdKxxmCBmqxiAf6EQc7noZ98FIdEsK\nNR9Q\r\n=LqAJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH30uWVd3SXEVVssmp7rT490VcefLF5IrWvxQ7rGT2QvAiEAnEhW0WEb+tsEyqTN849BRtWOZXtt6ucFGm2AciXIJVU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.40_1583049482160_0.8480984018762683"},"_hasShrinkwrap":false},"1.0.0-rc1.41":{"name":"plumier","version":"1.0.0-rc1.41","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.41+bde7abc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bde7abce742b6728bc7d2f826ba88b0d959a3560","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.41","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-6bkdl6x1r03egNJtI0aJjJuzYAXCDx22lBdcrrkYA+fe+LfKkJy3FrkobQBxsQPpBBUSmjyMM598Q4umbcnuNw==","shasum":"6a2b771fef7e8cd49494d4075d2a5884919ef37e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.41.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeW3ajCRA9TVsSAnZWagAAQKoP/27AunGfVaxAyUNHyQwR\naPn385mVvJpj9CMG/6qbaBFmjKy7zEAVCMsFUdJ5R4tLnJ+yQpa/qXjEt+XG\n0zbByNL128ROGTDyVKF6vvdcKQjuM+gjf9IA2gNtWVCvSScLoJdNLfpkLobG\na9smzI0KeWBF5BwhWv3NbHtZ4o6/lY9LTTTgbzO0gOaSaSmKjysLQr/ZxHnS\nJV6Jb7IA+AV6Sy634JggocthCjLpbwpVnITkTXSPKWZAMs1otzM4SMOwwdLP\nNpuhPigp0EwavxFRQkF1CJ7kyTaPNY+adAxKDd4cN2W+PbaidFIHyiSEC/9Z\n6L72Z59HzkLXIukLaDjSwrOrmYN596tarCqIUBxleIxHm4Tz0k9duCycFnT1\n/BHtlq6pK11uqrPzXwyD+eASefZ0RqxOi+6wqi95ngFwIi7wbWkPNA4SKxAl\nLQaqdrX1yB9to1HWjEcGfByLnuaVG1vJVaTQsr/vvjH04DobOwVtc0OFHwOX\nka+mhXaPzOZmIiedNWPUCkvrPCNcY+1NlClW8o4G449Rox874sd3m8JEiIR8\nkkbmmUwlg/nuFfep7S1YOoqVmgwfI9UBOIhsuu835jPw5ujNw8Q5Eaf/0G+6\nRSXo/KK9nhZcOwUTH3OL91NGQFu5N4wU1aXWgBlzGoGKHnbNATV8HbbX+rlK\nuJbG\r\n=K44z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDIxGu9inAUnnniPqF19wP9ooeRPqoOXQ3onK13gMCRFAIgCYjDcwof+P0Woax/K1+55XoCXY9Pvsl4BkdEKG7sjdM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.41_1583052450845_0.29589838190344286"},"_hasShrinkwrap":false},"1.0.0-rc1.42":{"name":"plumier","version":"1.0.0-rc1.42","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.42+5b7bbc6","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5b7bbc688dcc6db56a83697a1d7385aefe403e98","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.42","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-9kw0WWr7DuE0oeaULxJE1/ysHnTtatbaKPbxnVlzsiX1pxCfZzo45ZqKezVev6ESu+xcdZJfFt8by0mvbUnDsw==","shasum":"956ae6427aea8698b6cd0d80a7f1a1d63c1b5409","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.42.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZHIiCRA9TVsSAnZWagAA+lcP/j1MYeVcqiQz8TnrbMAe\nq6gR38/veLCrO4DnWgMki4GaR0p/NV+bDFplb/CiCQ1aOR4DPchuUl+ujgWC\n91u6lB1CIGeAdnwQw4v05WTwFzZprNNHUmF1FW7pAMfN6y1eN0W4yFvG07OL\naF9COXxki58luhUWqnkt39xOdgFnN5kL0C7mCK57RF99gBmb81mA00GLJ9i5\nU/o0CXpt/rDNtoHB/ShcwgXAKNkiu6YMkq6WDzqTyBg9hYOaWtnbtRu18lG6\ncYC4FCexAYM+od021IjJ5r+d24YUQ72ovfvpxNYC0ldfu7wPA/I3qjWGB37J\ngF3HDJ1mJ8yJFdiePLwGYRiiuo6YKpr4+GfTqRz0glZ8jHZxspYP3vD8oxCt\ndxacZqo1SVO1ScrpP+Z4mk1Pwof0aUtOw55pw196McFy1TKMqNJAhnckph+z\nlhSI1qwywiIbb4GrLdSnWRvX7hOjBT5ESBJwhXyUkawZHazrjHS0fNKp4BuF\nCoESkotG4Q9kxPcAgTW0L1FH3SFCcCW96s750ZR+wwJcUcxRhtDzpy5eg6aN\nz0EUhhLfwln1/ul97+Zx2g5swMyY1u0iiwEZfbi3UrbUm3F4TIAjhZfd5uj9\nS4pBKT8ieRniaxQonsjxND5q6bQ79HN8KlMdPRcbOb3RGD/MdBBv+Ix5bIQ7\n5dlz\r\n=ADzV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCYOj+92bHbiPFf8sm/iNQvAlYZCedbDGPrpYZCHm4FDgIhANvXAf3WhhDurKZhupxMtL1WTlnAdI4ouhDsijV2A1TH"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.42_1583641121838_0.8037688764509192"},"_hasShrinkwrap":false},"1.0.0-rc1.43":{"name":"plumier","version":"1.0.0-rc1.43","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.43+b3df47e","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b3df47e68a4188b9de2e774bd22f775c1b8f9371","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.43","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-4+W90tlEN5EtuddW1NUhY+BccAbDK4xBaEqkRxtZhCdtqVBrAvDo70JvdLuAyfz5iQEZghBBXAdFRHjiwg1xPg==","shasum":"dad8316e09da6bf305b5a8674c7803ecd916f33f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.43.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZHepCRA9TVsSAnZWagAAtCkQAKSktZ60ir0VA/XSKfCs\n+Nxn5LIJWCByfqHo6i3bC6b8itui6jHNgqN9H7d4YziatYBV5b7JR2qgdJNi\na0GINShO+82axofyCEnC3vL9Delkqt4OwtKlK7+cOghoo9TEj8G2BNniHVCD\ndFSghpLMCJ3EUYQhmbx+XfCyXRWoQGf2qvYfo1cCoSDzPsl3xWrWZAhDRp1Q\nnIwi9C1pPLMSP5Sq5FmWM6DREidw4NEqS5DM51QL3VzbpspKses95KETpONR\nBgvGnlV6mtDZioBDkvnSUAhzfeIM3zvwSx16EvkaG+FQeq0NSdXSMaPSKYOi\nFLoEEmIViuGKIM7S3wYZfEWmWki81t/Tw5PFuSYD2CVgA43jeP40KFgb8ZUw\nte31DceDAoJKRf6ozICLnSpa89ePzLtVr6AjEts6WmeTB7VU8EnU0qqdITaO\nU5wJsF7gYkBE7drjzKxDAZIFntt+9YNC8YfhUjjlIpO1PRH92m/lTh7TtIjT\nU/wBLpPzO3BfK9oIHJEuRKB+0eVbbeGisW2Nb08xsOAMemps5dLmo79DDeQG\n5DZ6apaxusO1YOxMuWr2JQgTe3VZlZl7QfqVbQ8q+Y5dOLepTEiU7H5kGL5E\nWSYzVp+1WcJrRRyYttXT+PxIlh76w7NRcj3zRXzbwhqqL9hLUYfbnn2VX758\naFmS\r\n=g4Vm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD1r1/rQAyuZjIOAWC2Kqv+fYVrs9JUGaJESyIa1JPlZAIhAMc8fEdOgqxETaDsrtpdO5pTNToxR+nNw5YEB2yfF5EI"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.43_1583642536803_0.9563585684806155"},"_hasShrinkwrap":false},"1.0.0-rc1.44":{"name":"plumier","version":"1.0.0-rc1.44","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.44+47c44f0","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"47c44f05c3b983e44cb75299ce83d53042b004a4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.44","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-7Wr8juL44BeumietVeKvWMvzUuBvGCLZcyahnEWTQsVkVprZ4kovdloT0/KyH+ADpbVspqt2cE7BL9462BxH/g==","shasum":"04ea8df1933e7a236f3f582562995a49517778c8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.44.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZJDbCRA9TVsSAnZWagAAQvkP/A/RXN2TWY2ZEO5YP/N1\nm4BcOvOJlqGW3w/pnUwUPAitYpqrQCHEFogSaqB8uhIOzKxW38eu8OZvKFoj\nW7NYeZ//dGupO6Pw+uxXHZK7zDoLiAlhtlFwRVjXEz/W7EjawncZdhE8Pxj0\ncOQ1cjZjXp+lohkIMTZuGF1O6ygWFadgOmV8+Y6iWv6bNzZ9dzm+daamzbxS\nPttSqRWTBqXBnGzo2Zh/62aja5EVPYku0h8QpyTA+yWb2zUhcnqfb10cy9L/\nPs7dhOANx7B3EyOv6XlO3cVMdKYzR5J6cx4s+N0EJbfRib6UXsJp560T7PR2\nEbRxRH1TKblcvdtNJ437RDeIiO94fmRrY3ru3/RFoDujafaYqFe2MXeIrPTa\nB+Va1FWf5yzAC4kfUP1LNFgi9chnpl5qeSR31YV0BY4iaXQ0X33Vu1GNTEcT\nQeHBULZcxWiv9witg/EgvTjucMaWbl7DWTLSxri976/TmKxVxhQox2Az1tPi\n7+IXSmng8D79A+A3wtAR/mV2YSTEgLejdZHUFQ66LHm9OO/VvpjnASWBi6wh\nkmZ40F7GPiqIesL9/Ok+oJdUeeLkZXfCoJ22l2QC0uGl5y5oL7yBO/DQUQht\nnLccbDXX1okR8pB5/6QFjNpGPrgLZquV1ktSMvORVz98HumdvzhAAD4mzwoQ\nMrTn\r\n=+z3U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDWRaARjRzG/1DMEppKOShG8jPKu9ajfNqpV6/+y9y6PgIgM98hn5Ub8oelXrkgiOekmvyb/DrKGX6XXAX8UePye20="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.44_1583648987278_0.5684490352411573"},"_hasShrinkwrap":false},"1.0.0-rc1.45":{"name":"plumier","version":"1.0.0-rc1.45","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.45+4f399c0","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4f399c0161f68b4111ea8e3d9c5b3ce9b0af13d6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.45","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-HNfBdSJ5ChxgnF5WNWVzcHOPYpnLz+UE+u1FY/vUExZ8jz8G1SkVp6wKDyCsw+xtxVD0s3o1r61GMSic+gjaOw==","shasum":"b2949ff7960f75211cac22a7f2e253fac8628e2f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.45.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZKIsCRA9TVsSAnZWagAAKrEP+wYU0KJYVTXt9W7OPr4G\n1lGDUCzYmsARrc+Ui+yEqDpwlCaeHKj9SPo6o0ZCLRxBFDpjYKTOAHvSb2Le\nkKKzCZb4jfTQuP3DIGiW8sCpDtDfcrV6yb2NGpf2IpZqCMGrd5g3dF0CVrqA\nQOChdwtZLVEMW4IZZv6LzDhemth2P4Lwzan5wrvWE4vPjOvwKmMEqkDQnPmg\ngaGgQLxtvVJjKKTgYhc2gyesUJNzkbMOTnhLIf3KDYSM2vBmbS0n7pjqOZiu\nRmdD5cP3QFsRTFGJZGGt7KEKbBx7E2dJqU5wR2tRVcTrLzH8CbXUHVtBvaMp\noDrps3QxMEWqwqPzvwrEhRceYQNmw1jcX/0qXNWiINKcjVY3ShH99ZoHlECJ\nyndlsjmcdOOiQG9lZGMS3ghWCV5HIEOWG3exiWefZzPST1MFjvPzeCtiTWQI\n2TtD/3qvjcqmI7tfBSMjKTJ+QVfCqn8UrZq5+HMxdBjJYuA6+GqjZTyqJc7v\nVcFClqbhVt/N9ENLSBYMShlcYVWzdJP9cras7XQnP0wXfcD8STRt3ZbJuxNL\nPb++9gEpP+qVXxXnKaUsFfrIdfDfTqLt9rvroYVcVJwh4CLelEk0OupZ86W/\ntuXdwv91gRQwdWYIo4dcSykckDKC27x6x++SgulM8hCu0l+rc8xawDlMc84A\nl22X\r\n=g395\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDK6lvOrHg6aZpT9c1DpbQPb5IRjlAvwkGevsCJhH3A7gIgahDu+Vw9bwS0fsD31XhNe9K9Mhl5RNDTNPObBDZLYdw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.45_1583653419831_0.9796567875224012"},"_hasShrinkwrap":false},"1.0.0-rc1.46":{"name":"plumier","version":"1.0.0-rc1.46","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.46+a4c4697","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a4c4697cdcfd094815ef3a49399047e790ffd9d1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.46","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-KYkZG2GYMO9u6AEYkZx0JXBfy1EdhVqJjW9NaGrr72bT1kZD4Qf0s9EOOQ4j7BU8m22isuLW+3oFhimcLKuupQ==","shasum":"8dbd7fc5da03fef63ebe4fa148086cbd63859158","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.46.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZxtsCRA9TVsSAnZWagAAPygP/RNdH8m12MP/buybswsG\na29NlfjnVGqWDmWoXw5Kg0PPwyN8mmwB0fRANnuSTkmDUtYw5cgbKeObPAtm\nlKVIXgJGBs5fqRBAm7y5l9Lxq1ToYWRg18DvCHvh8ZT/M9tIS+GsjKKWfl9N\njt6tNxwl83gUcHHNJ0OyIjLyITf8J0jhEgmvSSAIMXJSDgNOZ/JY+RkKC6Z3\nvTdly/aofj0fw7f0VnGfUus3r4TSCLGBv+/q7e6ek6W5JtmrUYmnhn0oOB/D\nksC3Gswf3ZfdE8pGLH7mbHcf/jCcjyBWKtuGCNVo12PbQ80lwey1yAnLyGK3\nzSrE8La/Z0Hga2j1mobUJPenzhuLjUd4Mv5lcjv7Vs0vUnfEw+plEufN3qlR\nU1LmHM4dxJ4fRdS88xK4z3DjL6YPfrVn77is24wxX0NH4dN9tHUaw2FiN+P7\nk36sUnkOZ6U7ZOEs2QdnAP6Aks8hHH+jYJKkh2Njq1BwaUYAXjtg2QFlJouU\ni6S61UhNGDLS4DY8vynuDv+IWuK4fh1NqHYwGYVkrQcNXZsct4HKD7C95FgH\nc3C1HVlCpnaULT0hxQ5KJMhaGWgbTZfRO0hJTsftZR2AbNZEt+XDjqFB4DIC\nKpblv1MyrUObGGk38YBjZW7Itb1XAUOtopPSEXgsZmpu03ntnbtK4DQBNREH\nOMIb\r\n=70dE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEmqtbptR66Sz8H+BpKbuSWXNFKzBciyQiWohnRnWHduAiEA/3uh4qS/v/1YVkAL5iYCzad2PMpkIlfil4wyICEvY40="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.46_1583815532213_0.5416980895821204"},"_hasShrinkwrap":false},"1.0.0-rc1.47":{"name":"plumier","version":"1.0.0-rc1.47","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.47+9a1360d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9a1360d7c9193ffd13427d54a58c14c27aa59f6a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.47","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-XOSLu9FWgz8Mbso8EvagHNj38KMQxIFrlsWEBB14Y9QhcMB12sOhFQ62F0pnKUj4Y6R99rGS50z8AlsfV/8YCQ==","shasum":"491e0f873dbde0f76658c8b819ef4bdd786f250d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.47.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaAWhCRA9TVsSAnZWagAAbA0P/1lPcZS+tgWuzvPRlwSs\n6k5i5boXcUI7YcoVJmDu7e501qkB5ewBWu2V3j2sh2bOjY4P9vtzT+L1IsYd\nzTWIG5ITWoSKOsGvnTjWZG7Q16K5X45iJopBNs8wiLb9/SYL4W6Q18wEGlMf\nHJMw0BgdQXTseTouQb4bf/YfJhsDxc2dSgO1bzHrvMqV/ZWFFSFTLzqQlG0N\n6D8HYIvdKyDCRfcv1raKalbUDI2fwT1gDvF9jqBXqcX1SUPeRG3Hs2ESwqr4\nzYXPCuIYbeBt3Dt8YyWZzPAFo2gC7qXHHA5tP93Gcp8WBrafa4VQVszF443R\nwo7vJizPAQ8m/nSevYYWBxRcswUVoufJE/vxMAQO0acp0rROxcLDVm3FfFq1\n9TnfvvdKlvC6GXoeAS5e8mIrNwsfCO6jKQUWGfHvVDPbYllX0fEAVoFfoyCz\na2FkaNof9lnVMINHUzbWqNJtxwE9NO2ZPhwnkkozsyxXuzV28fytDRB+YeiE\nW0drbffS1j9R/rK/mqfnSEA4Q8VB6ZqT2+xErDbo+94ne4BmqQa2XcFa3P1f\n03HUvkyBseDMOBsoeXmunEYAUORnsxXnhzLgkiwuju4Kne1b1T2MEXjyFv4A\n31booYAmHSPBWKtAi3bPyczTjt29TNoRH9tlMEgjoXXeSeK2ngUInKubMZkR\ngshM\r\n=3ihb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAt9nJmz8cW2Jch6Ln2224LaC/3xfAyjPCVTJb8wVfTHAiEA2O397GsijWlfvOMncB8isMsGydwGFPBzCmG/IZbD3Pk="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.47_1583875488604_0.014385361639224437"},"_hasShrinkwrap":false},"1.0.0-rc1.48":{"name":"plumier","version":"1.0.0-rc1.48","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.48+35e0263","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"35e02632049b91429570de30bd1ef31f8354304d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.48","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-kV1Q1TFnoUcmlawKyq7DnmJCel5ffkOGB49+najwQ4RWOS06M1qFwodFbf07oaq6Ewqe2Z3Rxt7OMrMnGXRcYA==","shasum":"f6cbeb23903562728ac764e560818568e463a16e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.48.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaApqCRA9TVsSAnZWagAAZLgP/2olMPo4ECmjwjKNWC2W\nG3HPSz0BDFWS3TJqJev63ESlfc8BRwJHBSnT0z82wDtZWKYj0F3sNWZB8nAQ\n9Z7uLyLL41FqlvJ6dGjyP8jKGXyvUj8sGzNGweuAjhDFK5EBuX/iQoRBTibA\np3NO8MEAJqdOjVULQNoWwTctyVW6KWKadVZ17THjuvypL8gQ+NipM23qWEVT\nwRPg2ul62/sjiX0E+IQ2OLiWqV1fzhxAlgHO/zZVUn/BExQDia/rBCiAA8Wm\n/xoXvZbW4VT02gMmwbRaeUR4plxhQ06UNKRV1hUQ7R2vwZRmZCnygpT5T7T0\npjjgKo3iy+6pdQd+/MVL4yrHFP4p+7iLHmo8NlPyemmdYwLrVOurFzLAKzu4\nH7+yLph4MuqYu4TJPuWdNL0eVQKPqy0l73jQ3EF/KIrif5APZcQqf2kuxbbC\nuOdi1EwcX2Td14evoXO+oXHK1wf4iLcqG0pjT6kVkf9ozD5PB9TC72xb/Fzv\nr4buBnO8KrX1BckmhoVXp4/xlTQebeVl2RxbODLG+IIV2DHh9TfGlQHUnKSN\n4AG2XSOXl5Z0597VTLkRzSEOPgaJVHM3eKtNUh2nRa+s6YNuHvfk076mkrcs\nPM/yEOZ+Pswk1qbkGhJlZ33yMTbQl/7eSaz2OvWgwGU2yeAjchb1IfrtAn1S\neMiw\r\n=r567\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWje9iN5EbpHdlarTruVzJytJ0rl2168t0JmJttAVjcAiBlquuZxhV5FqfDd5xKploOXooWGYrkIhCMLWbC9QFNfg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.48_1583876713931_0.11592503429260792"},"_hasShrinkwrap":false},"1.0.0-rc1.49":{"name":"plumier","version":"1.0.0-rc1.49","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.49+9520748","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9520748bba333fd56e04848cb90d351a87bed5cc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.49","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-bblP2TUb+MPX/yDFPWp9SfxUNzQvrKdOmGJtm0sLUZvJbnIW6NwqQYSlWDC4NyyRa+K0z2WW8DTEs1vkb8KVKw==","shasum":"bbd8c0bdb65971a8069a5843330d09baa2ece08a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.49.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaBH2CRA9TVsSAnZWagAApLsP/RRtT5LEW9xiKLMhKcfP\nAqSK7TzzCwyUoeY6ATGaylTmnDhP2UNjEJKRVP+d3AijTbKeLjfBGH2uEt22\n0WSC+rNXPcuwcI8gQrlyA6r7urC8nbMur1gkabGso475OX8+waVT+ArdojNF\nNCC8pGFNIZWwHSP9o+fQJWSv8GklgoJhBXCeEk1E8eRydOIzMO2Oy4dYmHRO\niyAFDXzi+LxUyh9TENr3jbemDOfp4SsggdUMQHpiJdbI/B8GOhGLA6OY+AQE\n7js8AwD9Ne7occE/29iQ2e8GNtiQfQ5HV2rjuIFek/KYmbIi57OKy1gjE83i\nkFbY6CA1P208LJCDPN7ciNsouLXdmEqoaSJEOEFUpvHHDovfrPk1Q+m4IaVt\nNCojcR6nG/UvWA4epKwXmt4q6AoWtMIBuJ/48RIFKnfjpzcTjOgbSvO6qB7m\nH7kVSFtwEW6v2FARdK09WH9LCO+je9iofCSSMsSTC4VJHPDV/puIn06V2s4x\nRrrp+KTB+Kv11hBG6xyTGJZRQENGo93S09WrIk2gRPzg4cmNHCngaJlDcIa2\n3vewpR7vHYKfcdFdryy2L6UI4P3u7zhxPLtJ9uuBs59T1KpGhoz620Fuzkw/\nswjZmkvIikaRs4egAwhLYxGeKQH+L1tNAxvVB0joAkDfo+OiFbe1LvTJQgtu\nvg0N\r\n=dIs/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAUCKajXnCvfA0RTWS36KfEXqvQGPHWMoE7ieIAaG3yiAiEAyl4b67awL5E/LNxooFUEMYU2XROTtk6EeFYd67dIhO4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.49_1583878645866_0.7474403335714646"},"_hasShrinkwrap":false},"1.0.0-rc1.50":{"name":"plumier","version":"1.0.0-rc1.50","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.50+d32f7c0","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d32f7c0e165dae343a806ece1eb66b68d9fc57ba","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.50","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-sRaoRFe8VVOWQsXRrhfKd5vGwDNC/tZgWslzWwAdXdF8BSbI7kipZGzhQFCMbbktEWiCunye1/raMehAnT9v2A==","shasum":"8a7c6a4cb1022c13dec7adf8ef533a3dc4522321","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.50.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaLENCRA9TVsSAnZWagAASMkQAJaeNM6kPXo7yihYub3J\nix9gZbYc4ScuJvF9N2QPHJinj2+RvCVyBxzEvPze/J2wCX0VqRmRR1ct8W67\nfPP1FC7UuTDl/+mYg1cqFehcexVJcDUCQrD7/FvoGVM9G/CsE7CgpsoDvmfh\ntdWgA3cYdjzx99T5kWFWBnRcdnjdyfqXzSsBIpQZiH5z2SeawReTWlP6OVL3\n8IxGsvyU8DlPh8k2wyK1MYoET2kfEV+nmDSyWIfNOAPQVPSQj8In+G5w3i3A\nCTO3TRe/Kw49Nm8Puo4AQfzYDeiWuZn5mPRCWCscpT92oZixaBAHaqLZn1+1\nL0dQ0tJcyT9Tu//qSfzDdigL98t465QpaWg5O54GXwE1qrjVPRABtT1IoCJ5\nHbNS/uIDVgShM5sAhH/8Qhex86b0DfHPKi0qAOAQWCP2g1Tt2PzBl3gnQ31j\nRXYj/4h2aEVenWpG8dQW5pyaTfg3NvZvX023tzkX886GCSEVkP8rn5e4STUk\n81C0S99anxehIzJxYdAD8oJu3gPpEPuWx+/wEtPFwaook6MwR24W4DovpPsx\nOm+I1n0ZgwQJHhUSz3YmkBwcEtW5oBNwTXqDvNM+o5nwJ3X7pz7+KzkKRMJq\n7ILuVzTP2SJwaQ0HvJabm4J27Uzx9VibUWq4NrCFXaALEbPBPl0Q2LU2DuNu\nK0y2\r\n=3VJm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFrgK4Bg7J1kHL6One4aXo6W3AyMBIoKUhIxc0uFlkzrAiB8IJOQTfOTqcN4EhsMh+7/xYOvicLAZ6biVPaNmYoT/Q=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.50_1583919372844_0.10068952258536545"},"_hasShrinkwrap":false},"1.0.0-rc1.51":{"name":"plumier","version":"1.0.0-rc1.51","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.51+7ffc1d8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7ffc1d8048566ee927a91d4a3ee19ca8d9576257","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.51","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-hkK1iWNkdqfJR3yDVnYcibatbVMCUuCbQvAymnM5xrgmXBnUmaymZ7PEzqoxpcQ7zLWU1hjcgF31iRmoWWZppg==","shasum":"1270884cd7d834c962ab933822582bfeadd5e0b4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.51.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaVM5CRA9TVsSAnZWagAA7kUP/R5giqAI6qNPQez8naVK\nyJllmPBiUXqu2000g3qyeZOmmtmYZK46bnlSQisn/xZChrLi5QbAzpxi5ItN\nWMVpr5TucL3LdtE8se8gjQYFQmG3zWxb4U42SkGt2FHHTxFJmKdQyRbPt4ES\n9XsMMCM/YRD4DvigEPLbK8RSQmGmcvj4WdTlTi9F/7JZpqWRlQ3WIHnD3Puy\n3fcxN1SRddWaZSev3NgleNf1e4MLvTfUukmCgBo8hSyWVPJI4iWTzAhUwvHl\nj9uDGaWZ0V4FFSMNSvCNfMzFKIUZvQybQahc/bngXzXws+jmiVuqf4QTRFBt\nBpeZBeRBOzup3OIEdDH1G5JKDfmf0+t580r3JJbLBg3xp42sekzU58iBYR79\n84fUYNJQ7YpvrqynNY2uH8bjHJiJEna3BGQ4+h6deyO5GnHbb1uXqZdsQzuZ\naqA3umwcHwZrH+8BAdg8TBCjfWxcAUIGf2BCQ38CdbnPIuN6sfdHVbSUclms\nHizkqkb0S8Xf0hLWR31FsZZ9dUmao1EMhojs+bgUieuhL1hEFUh6+hqrihH9\nB68T72I+xuYljL1rnWdNO0n6PzC+VlXMXHftb7HViQuRW9AuaWTiH4hfV5Ip\n/DKurzI8KWpNJEz81koqLjHQsfqhXPOxk1d6mtm+SC3fCmmNt7ng5Bv4ZCrr\nnnGc\r\n=A+ID\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBcW9LQzuEqqDpLbg/w19tbDWzHIgfCyo9gko27cXlUJAiA+TSQHmjIJQKDpCek8qzEWH+MDvG2ACzjd8DyEBCEFEw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.51_1583960889040_0.7278606753036729"},"_hasShrinkwrap":false},"1.0.0-rc1.52":{"name":"plumier","version":"1.0.0-rc1.52","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.52+eb58254","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"eb582542272bb30948deb6da0534bf9ff88ce024","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.52","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-yFXt6yPst1LPAqzL5CuRkjlaX9nT2W8eWSG5PjTVI/42ZheQlEjDYEyavsrNnVsDD4VFM1bltJKZwthwGhCZ0w==","shasum":"130658cf836d821339dcdc6bfd434764746b752e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.52.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaVxsCRA9TVsSAnZWagAA1RMP/RWPdRhLv2XEkFzy2dtp\njzRzJaU9r2RavrvYyO/rK6iC8A7FDtSawUy/obTR0rBpACpnjs9kcd8MvpiA\n07rD3EnMHOJspopK0QzdEVBoGI+XkWiNzs2cBh73qqdZ+QkwXrWlokzlRICS\nOyQM3moFCE5rjXEBwb7SbjoEbSTsMfqaeFDoB7DBJ5yMFhXPaGFEpwIGdvRK\nRkiv363dY+KIsTxBzzBq8sCjyhJ9UC7629MWVmPFZdeXMni/cPTpsNaZAQa3\nIdwGwo29ctoGDit5CHNSXeSuYIylqsXdhnrHJ0TnyhTQWbMFCmArtxck9TzK\nC5sR6lj/mkHIp+HhCD0GOc82uBiXlDFMXbjtrLXrvuUucuMbyQrcWWCLJz+n\nBjoWsRvgeFrmQlNOhx1+KHucmJ0b9qpaNc9FW4zrQ7qNZfDDjrDGznq0AdQ6\nNRcJ4YybtoZALEnsJp9wkh5krbX/eZB4gL5tb87pC1mePLAqLKDE62Xs+ZgZ\nQdcZlrXdQ9/mHy2CCv/aMR4OtII3aIDDGFUotAaSbSm5AFsE7wmRp0XQeEGH\n7KIoXWhDtFCHRyEtesSqa17Opmp0sNQLrmBn4OhsYwBQC+jjfbXyxeSiaC98\ne9w6bNPLaGWTCKmdsL0Z9lnPc32JnTzNE+G+EV0NblZuQwbL2rCDpwvPWOq8\n4eWZ\r\n=69Jw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDx1Xm1PCnLPoGENmThi5EU7jqBbSXS1Mwo5MfJkoWRHAiALy6T+NeaDysfY+pIWBp/ZMwOcTL35uOtbJnlxkBqPMQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.52_1583963244242_0.3288173834603243"},"_hasShrinkwrap":false},"1.0.0-rc1.53":{"name":"plumier","version":"1.0.0-rc1.53","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.53+b4324dc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b4324dc007ec5ef7d6ed580c9229a456bf263d21","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.53","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-deV+a7+BbdQDhrSIFCnskmJIl7W4XX14ib9YaPB+ld9kBmwE1DVv0XVwg5IFInRkYcKIQNaF1CqynkIQbGBo7g==","shasum":"d8d0363d7230bfa2997803c8e9b88c5fb0cd128b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.53.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeawpsCRA9TVsSAnZWagAALwAP+gKaQwLElPgb6wCdS+3D\nefNal704p+kuDewpZ16e6ByrKkjWq4y16gqvzazWcKow6JPZJ5ZL3HDvGQxi\nYgEsVKhnXAGcvp6nFJEUYaq3vevarQZdFFl/Ney/MbX0XWDiLCuPQOR2vX6s\nlLgF49MfzZc/XvmjdoV6dT5RpB2gUZazTty5/qkRP4PdQEl8P4NVc+wHaypW\n0+ZYG4PWGW7wgQMcN2Ur/KRVaRKrsUYR5mUh/n2/Prh9BUac8LdRybdXG6Wh\nWhXenkukU0deCoIUCpg8G4HzQmH/FB6xnhwoxp4P1RJEjtUncx79Z+ncNa3v\n+W8WUyp359VNnKqSKuZQ27329IqMCRhYGYphlgfWM+pirIAE/kU2lSAlNBvH\n0FxKSqxkUeOByUHKNzedQI+7+7qCMDVGBOFZVcnfYPrHsvZqReRxq6rwq2ud\nTQrdWnnbgFYNVEqXSFWCgRf8S0cRuy20mPppBdI1YuDmj5ksrf6pADXjAFTV\nCc7jWyYg25L1O8CTT2ZS1+EG01GZ6sSI0qdxLVCHAysdAItcAq9cjIIwvv2l\naE1ro0FSaoYswURRZ/Xh74ktJShOi9qx75OZOSMJkMlHtWPS/LQoodpOcc2J\nxQrfH/qfUZJyOy8BMIdXBaIiszCxPDHikaaYwEwFgtU0MTNR1znzsjqMLev7\n3NJ6\r\n=8hDQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAm1Dp93SxMOH3ShS9n23wnUlPeYYy7nrim80Y2q/hhnAiBU+egYViGD9P60fxqkDuZf8jk3qlsN2cPQAcNi9Z+SXQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.53_1584073323553_0.7694691968561946"},"_hasShrinkwrap":false},"1.0.0-rc1.54":{"name":"plumier","version":"1.0.0-rc1.54","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.54+3ec6f9e","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3ec6f9eba5a7f999c29535529777390104fa9729","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.54","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-tAIf/raDqhGO0aOUmGLmUOiDDj3pv62bWrofr21xmFuGv1aPBu1E/1Ayke0GRes3gL3VK7bCVVTivh/vH3I7YQ==","shasum":"d76e76223aafa950503d3812fdaeb0217cbd8d62","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.54.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJea/joCRA9TVsSAnZWagAAehMP/0tOZbH9BjrP347qXTBc\nuN4ACyTwR0BODe13DyLAYfaUFOtu0gDGOHpaWsPjH7EWlH508b5fbv9rmwLP\nynym73WvdykMTjMzHLVoN+drHYrqX2qkjfAqqbymbTY5ufdwpeQ0OzCaVaaj\nQ8qSw9TZnMYJEhhnXbG7Oc+kj+Vw5zNPAeNTM81Ht2OQEi4pDOaYa8g93YJd\nfn8YiSY8aX3i2QtCyZzIeimJjngpDYKUW6rGAc74i3UnCng0fzMzEBwYwqFD\nZydt8VsafGdINchgl36UKxat3Siu80mpRiA9Hn+TZnoF4Cv/yF9U/5X/tRxZ\nmirNC1Ffe2FcaHD5CY5ProXTYMZKA2PgcyxpWveczgy1kTe1yDzxex/cyL82\nFZGoXFfzNBfM5VKdwdlYaskSpuRfFJeXyU7sM5OnW6iWM3CtKO9PqWFn/mWD\nzpwyokYc9H6D8cHp3bjxXYdnoQFAMkjHCIEetk+zcNRB5mYzQoJgPJgmpNFs\nMYWGgHLTgWw7/Ju+Qf8v5ifqJZTgq9sfMQuaUDIpUHoEauZbU6jJ7ngOeKqK\nhwORsxciY+wMjvQaqtC0QdItdFqKxaeHWI+/Z0xBW+YCvgPzQ9sqFX7Cv0vn\n0Dfta1R43vXcmo/+nLQTxggmZ+xBqGTBtC0mqjpEIPKjcfZ3c46zOHFMhPNh\nWcti\r\n=qlQI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEp/vauI1qbN2mQn6NWeS7U0S74+DoezmXK8Md6qKFx0AiBvwtuX7Pkrbonv+p4i7EXBqiTfzrDgxFa8PBARuSGUsA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.54_1584134376352_0.20425823073403326"},"_hasShrinkwrap":false},"1.0.0-rc1.55":{"name":"plumier","version":"1.0.0-rc1.55","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.55+e6af483","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e6af4831954f2b6fa0b396e54071e82d0b3d431a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.55","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-SPtSzQpvMwmi10g/2fiZZPdy0JaPGzmXhU4Xf6xoQlLJqOGXFTx9V3JtlZqnA65DLltxv1AxAetM6IJ1nWQBGg==","shasum":"6c2d5bd3d682e3f810f0fb0545db483d2510723e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.55.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJebiIPCRA9TVsSAnZWagAAGd4P/2VWYjnzZt6OWEQ5RZ1V\nMc6iC3ewAr4kW3mGI8zer17uTwdmJmZsD7OO8u36y2zIMyoryu3ti04VVohA\n/XkGWpv9rnLXQXggxMoDfmxy6sMjgza8D9paFdJmouthGzn6Kgic+dfEDn6i\nBvV7XDED6r8pcHfPGdn59kAsCl2hHbSXcL5oMAOB1IskrWasqYyzpnvMfXdR\nAotEA8rO8aHa/7GtUBvmtDEd9yjGEq/pC3tEOIc1HX/dT7YrCdH5LrXSi2rW\nScO3VWbSdiekTuxLsdejUkNTCW7Yr69HFBhMCwRf3PF1NY19MZA5sae5/Wff\nvx5e6rpALjpK0fqK7794ykUWe/V5GBYA4f+qvoT4alwDymy1Nwd/d2XR1zLF\nlpDPWC33U2Rnyhg9tdmh478xLIIG6/YtoBS3Ktb7iAbVgTmT8QxD9X+ZrGrP\nPYlyGOB5aPgxADrt00n+aUNl+bUYv/802wo/4IVhbU04ueFKaYvQUfS2oUhG\nW87nRT2H4JWQ9P1E+kk3Z7nccLtX1mVKZCkcy8HxmXNaPgpqXgpRkZMZ1akC\n2X9kPBd0Q6t9uM/B8uyNaK8J1t01ZaRiln2/rZcpNjpFUFhB29XAwP5X/LHW\nfVJXMlSiOZLDjkK9s4iKKbmJ9nHbNTgO4B/2Ubo+wV7JXq3qyDG7ap4iJT1n\nLI/H\r\n=Lpne\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC8Z3M3HKBdtV85nEryVrjq89H//yJoGQQHhA5ySDalPAiEAmMaHchorly5rIH1GJKlPsfiWn7ytWPFlxoRXI4FYueU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.55_1584275982991_0.6907213789408606"},"_hasShrinkwrap":false},"1.0.0-rc1.56":{"name":"plumier","version":"1.0.0-rc1.56","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.56+8b49024","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8b4902471595171538e1b4905d7091f34eb0f9b7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.56","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-/U1DwMtUJvaY5HxuOKSL/yCbx/bHh6Ue/dnM+4IMsXQhSUCAa6mSuS2xDfrHFdMu3dcAFfoARXxiJHv1l2abEA==","shasum":"57b95689823d61371800bdd8a6541c8fee66200b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.56.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedv3ICRA9TVsSAnZWagAAknoP/2ptKr4+k40S8nA9uD0F\n6RV+WTabxTqHQgGM/ofVSOlhBXa6FRVrkJOTAg0blZ7FSMCNkyP5m0rV32MV\n0OzBXXM+5S2G/FUxUjXPWtG+Mf+pIbWKnSWIj+e58RP/xv5nQ7KSHq6yUstv\njkhqUCXBlQ3p5QpeTJdrxGUPldaAF2FRJY+Yn/LP4N/KcqrxuRQIjaEd/HsO\nlT2RdbWaDad6Vw+HyhATHmNwxbH7Yi9AzJGd0Utv/mveSaXTDstipZc0tKxP\ndzTfmnqhovozTYm9pZZVCIpsqW/DXARu0wDJyO0l6PZchzXXdV2O1JBI43AP\nTW+WS8C9kWcW64KzDKHVgyNZU6FOkCRuGDvl/secw0OFy7BnyjKFBkDCkipW\nWDzYOYc2M1qz/1GrKn5FXW/t7jYmoa/6oUgs6P/mEEdQHzvZ7wqE9OHr7rNf\nYIsrI7JrJ6pwNl8SfGXCEa+VGC1jYJ7Um7BwvsyhDWrQgYDwGmGMn+JfQDjY\nGc5jQswuHFk2k0aYZpYu+XZNTZvlYgTtJ5FbWUIfVg2hYOi8EV6b7s0KP8CZ\nrinEJSQBKCZ58yws/jmQ75bknJQUhHQBUssGy1dOaSsVqJL6OvODAViZs8EV\nKiDGbHLXX41rTw5wwkTE32FYsGxjABOymHw0quITYSobZU96Boh0Qq6L+VWJ\nBTnL\r\n=e02b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEVgOjc3mzTI9wNko1MEbnHJL1UFhMB9WAJl9jDSGNTdAiAQrOiZvfLwCh8o+f0kN5DpN0hzkv7Rq09g19tTnYOmJw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.56_1584856520145_0.6737419120647066"},"_hasShrinkwrap":false},"1.0.0-rc1.57":{"name":"plumier","version":"1.0.0-rc1.57","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc1.57+cf7f058","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cf7f0582a1a1fca24e81146c56af06476029d217","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc1.57","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-fbH+sFW4DfEjWB5Gw9MqzzU+LHT8kjqW67UEac/Wjpsd5pj7gAReTAPZcnDwiFCsP0rj8ircN074XUX1IBIUXQ==","shasum":"6fec3e1b5eab6d1d83caff074055da19135514d3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc1.57.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedwIACRA9TVsSAnZWagAAaF0QAJDjKGkrCBOjx9xd1NCT\ninuAMjB76wZm4I02t694MU8+FmUI37kXSWswyrISM5UmdTDEJXaH6PMxkZIe\nZ6ekjMFjw9AVxst+KvIVWqb+9stIFeu6vQfddA7kERfVpc1DC3pjTOd5GW6l\nSbim5YlSKItotQ01cYBghr7esFlEL16Oiiz2v105ElJZ3PU3LJutWOS9tTfG\nniaM16hd7pTG4TUUIwiHWgCv+RSHSUFzzhLIa/5WEr1WVP6GDO9LSInSnH5V\nbXBgHP1x78+ECEeTHCCozY3A3EV5DGt+sLJOpCli2Se8iFh7yxv3eeIsitNs\naoIbUnH1r/rIK3kr8sXwV+Np3RXVLsFhgOBVD51ZmWipUiRTFThl0BykaU+q\n3ecbSxQthh7JxVmFSN+dP0bDvorjzG/FMOqngQalpGNTaJLKpHfCRxuKUbQ/\nEAh4Wef9QIXEX+7grwpROxXFQn4Y2ct9zBTOBGKMTYDUjQzKR58toKOEOIRK\nfxVX148jQ2yZ2+DTigGaHkcfh0spptz3f6W+1mH6HJ5getw8kdvakqqwsOfV\n1MWPxBaoggnH7b1aRvcPqwZfqdDohex7fJvBdbURsTSrn1/lSo8w1/TGk4H5\nbbhzWFw3W3tGb8hCvvAQ651/QOjGRojJdynD2sExVnyzskByM4r3Va4owC5D\nfp6d\r\n=5QCa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEnGcESonKVmkThlaSKKfNqydXp2BWl0a443hFyHJLVFAiEAko8MYOWjrwv5Doo78YQm4woQcR6mSQmWFuv0Mdo9+/A="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc1.57_1584857600001_0.42481319322729205"},"_hasShrinkwrap":false},"1.0.0-rc2.58":{"name":"plumier","version":"1.0.0-rc2.58","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc2.58+55e9477","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"55e94770f1ce382dff11d9e0f1f5b2f467231ce2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc2.58","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-+ZXt6QxAFc8RAYKDEzhMGWfV06bvlyO7W1syrXcYKVbszvgE6DjQHPLeIZG8E8lrABYbVyCHFA8/sJXRzGPK8g==","shasum":"42a194caca8ea94487520b5f9cf021d675085125","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc2.58.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedxA/CRA9TVsSAnZWagAAHNwP/jvTKDP4BwSuYjVs28aZ\n78GE41b/CGms2h+ZBbLstw+Fxd3EFZRND+4TeeM6hgfx7UCQLBKxiTK4DKuH\nMm/6fn4cq/ioLcT1T2XP+PccXYlfvUr6b6d51m1yHUVVhR8Y/T1E9DW2KfXJ\nNj+ysQeQpuKUdUKNZMT+8CXKp7z+slpX/JEUjxq3+TKDVDWU+HMgBgSGj4F6\nUPfoBfeog6ZoSDFEe96HzvqcXLQPAhj36js+2JDcEa4jLzZIuA8P8U2LQswI\n5O7eQCboz6ZA4pOxGHXwP5avDlCWPye0ye7aOjQbwyQF1IvZHESpmeQcs/fr\nUrtyVMZxMzxb2O8G/O7m+ZX36pGMEDAculmKnvHfsEJw3Dg0S21D9CeSZehX\n/fMncYbpBGmiUtaeMKvyGWibldRyuOFxOZG6UGbWF6+u0kmf7+YGmwpP2pCq\nGzLSvU7/esDQCUXHnq1DbavuDklquoy0qFKsQL1Z7FjxJQARQWt1if/OhRO5\np+xCdHhCPzA0ASnTpv9QMp+LPFCsq4VrmOABJoZ1vh6lr5nfeZtGchAuGVml\nYQiGuRUenTXMoHVR4GHoOE65gmy8EX8CedceoNxGEOIF6B3J8yvqC2AKMl5I\nmWStd7yGbUE9BRQOaqht+8d9/cWClp0GL+wZzcV0ia5dxYGsobk9yxPQLjJK\nZAnK\r\n=JfXN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbJeaNwOwI4+j5vMMxLpsMDxUH0hm+rOn7rMgQGRfBGAIhALSD7+7C5Ckc7BJrn6sITutj4qPiD4r9qHvuFhRQ/r3G"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc2.58_1584861246853_0.2854873579535906"},"_hasShrinkwrap":false},"1.0.0-rc.2":{"name":"plumier","version":"1.0.0-rc.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc.2","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"779bf17115cf7fefea315219cf282e535db877e0","_id":"plumier@1.0.0-rc.2","_nodeVersion":"10.19.0","_npmVersion":"lerna/3.20.2/node@v10.19.0+x64 (darwin)","dist":{"integrity":"sha512-I2E27qhzRaxFjzcjv8ADJIcvvmGSBOf0m8Az6Xyjkw7BwCMMPtV9Vcd4PVXweZzTARphD9uCToWBuPjyj47PpA==","shasum":"bf4f6e611ca37f9b35c4d38bca9e550a92e91dba","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.2.tgz","fileCount":13,"unpackedSize":25759,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedxFMCRA9TVsSAnZWagAAfCwP/1qHXAkJPUzEfJKiusMd\nsx2dJd7em3mElTlpubG0IFZdoX3BfYcIfkXS4SMaDKorXpfeNDt1fIMhxJG/\nHlmpVOn9e2+gcVCjpQbAoBlrkJ/EirST1/Kvnfhd0tu1DMNHZR7AVl7NawVf\ndG7vfqNv/m0gLyMGd7VBUoRcCMCRqHazCx8xa/ZZvbCQ2f1ytOwtSz4B3HZR\nUDJ3GnpNSSOf9cH1jg5CSEMu0yW5tbo/MXwS8dfZmJ5xIlEGFkDarqb6o8hA\nF22WzHyligO+oNJwpR/j2cslEkKQCWTsROFwyAOqtIg95NGQD3zCTBycsxRR\nGE2LgkbVDS6Es20Ud0XiejOI9+Pc+JN1bAICRmd7PBaMNoZlgnsMxQPmd4aj\nJF8bkplIiB5bOdon6JcStfX8Zvp8gcSa+1SXuLQ/unRMVGFC5oPtqNN135Y9\nBTOo1Ql9+WRbOsCEMs1T4gdt0Ed3kq4hwYfX074KlqqAZajvQbThASZSf34S\npDtpVxCeBdS7fY47FLbKop94NSRLru4gNUWd3JYlzvPdXjPdBO3LRqdGVmaW\nG+vZ1sFPVu3xTE9lo9qUuIQQNXgRs9Yiy2Sii14j//Dr+wEaTFfh+P8fDADb\no3li3V+8BD4e0tDl3zP+8vsK2hnleCCnmhVLsZ+eAN5l3kzW5p0suAzznK6T\nguRw\r\n=uRUk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDI3cdD5mwwhmhTaZvRyDPb5UaXWsBl1nboeAIJ9ZI5hQIgFLxv2sB+GYJlwPu5aS8gBcQe+DNiIC1K2EUM2gBW3W4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.2_1584861515596_0.3270890357764886"},"_hasShrinkwrap":false},"1.0.0-rc2.59":{"name":"plumier","version":"1.0.0-rc2.59","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc2.59+f9a4594","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f9a4594cd6e1122b939263e24803d7c3c761eee7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc2.59","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-pu/UdaZ5WzspyTvV9o/da9nmsxnq8/Ulsf9iyOrj/2Ihu/B7nKCt4KyO3yjQ4u0BwhAn+NP3WC42S9fvguFKQg==","shasum":"b2ea0715b11903f801d01867b0f680a380d41737","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc2.59.tgz","fileCount":13,"unpackedSize":25779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedxFiCRA9TVsSAnZWagAA+UcP/jecSLorvrIRPovh1HiI\nyiAFrPUWszcK16yM0gZa5r4VasFOvGOJ15glpSTXOXFYzERGAlDVGkGskZhl\noMuDzQUYKGG55ELomBSFT7Bdmu8fwk2/C1AeivmlT36lG2GYGhqxX4sN6uvW\nklNNSXhV9/mD7FLFe2+9myNSE6lLJYfWkKKvPA/v6VmqTOJbJB2ofMzPokCf\nMcdNBjLcyVIb9XOXEguCiqduld/N/jheazrHT/R1hvVY7Frb6DMnCEO2FaJQ\nTt196o+W5NqJALi0ZyowlSoIf+7fsVTie2FnHZaU2/8mFQPyGINkuKEmiOQc\nEEYtN6D26A1X+zBu4p5f8icYiYiCkElcAokks6SMW1ORuFnlfm0DpJoPLrdB\nMebRsJ8KpRgOqTHMTz5YOD6FJIcd4vLnc1XYO8FMUZLM6Qm81dlQVtf+IdIP\nNFgNzRiDWjiJBDczwq7mjY0UixgJEh0w9CN0WAdYrZOC0CRp4hs2kE9k7mAZ\n5jYuJ28BzCnWi2btHzRGSus6jBFNqtnGi62//DtGPZrciCgLnw8zpA6k3urB\nGKTX4rD5iu0llAhcgO+hM9ujhAVWBe1LLtaC8mV/5fIjdOM+JgPM2/o+/17y\n61Ev5C9xMuM40e/MOoPbdoIH0juV+wMbnnn3QIaSnlSpHwCTNvLV9H0G+WQ7\ne+jn\r\n=rqTL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCI8Ne+4Q8582JOCcSghN+sZCCtO5XmwDnt0OM37nfKgAIgKArGkl4MY0ggf983Z/L10fQmJP9npzfzds+y8Y7ZqWA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc2.59_1584861538467_0.8113578756337261"},"_hasShrinkwrap":false},"1.0.0-rc2.0":{"name":"plumier","version":"1.0.0-rc2.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-rc2.0+779bf17","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"779bf17115cf7fefea315219cf282e535db877e0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-rc2.0","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-H3Aab+7qyFhChMGttlZ7nczJ3CBWqNrS4dsQqnpxZ8HU7rdW6S/IjE0iBE/7agU2icB1GggiqlurHS8/+tgG8g==","shasum":"2b354838864adba0ff3134f5e034e4c06b026f2a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc2.0.tgz","fileCount":13,"unpackedSize":25777,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedxHtCRA9TVsSAnZWagAAMiIQAJNeSAu5oHZkH1zRj+yJ\n1e9pFPU9JkmwFPqarmz+eW6DSkyP9+yH7VGbE3P/Z55WX1OvCG3tdtDEcRRO\ndUPCDP9s/7vHedbluya/8lmSi3bwIOpslTD++yVatw/oO6nkdFCG+NLxRNjY\na5YeXNCDW9BrAp/jPXgrmXSxoa2ZJapGkx6OrNp8hwxsfaqjcGYV9Nhi41tX\nPGjflZsdzti4Ita2MW4FqbyuN93bMn0gu7h7Tjvn9LQtxuVFqVFnZo/wgbip\nKwUYEpeK4Tna17DVetcPczNQkNdEPscyBbmbEU/iiXm/kGoZj0dPLtUsvlCr\nJ8f13SFENbhH7KXJvo5i3xwSwxyLzJlWEwQshN2tjxAaQ5LHqvekH6TG4jeN\njIK6IN1ZPfwSGFACiXvmsxHz3QevRx0DdWIodLHoI5LH36kVOwCDsZVx36+7\naNPKLYYRUDI//6BG42gpjifU164E7yTRQ/Vv9ADYF/Oup/9+S5qw4JsC4+oA\nUezhrqDwvl0dB5dLQRjKQ5Qg8EZwYlIx5ty6XHJh+w+BN9LcJEFDVNknAARc\nqnED3EAZvJq+mmOgRf/0V0Af+15+jeN6VMXe4eIFFmZ9BbDySAa9Qg7XIcj/\nUFM/TYjXcqWOOy0Vo4GCr0bu27UsFJRiES/n2/kyU3oLrKU6nXIck3ND5LGn\nV5Ch\r\n=Azjl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZep9zdzZED+jPZnImGN3Z1mOQqFZeU/toeZWAJ8++agIhAPlnGXuuJLbfhG5PzPOLQqK/nCUNhc925i1/ZnjrFWLb"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc2.0_1584861676628_0.7119833354120217"},"_hasShrinkwrap":false},"1.0.0-canary1585436324.1":{"name":"plumier","version":"1.0.0-canary1585436324.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1585436324.1+5cc0833","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5cc0833100a04467728b21c5bf03038f74c4dd81","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1585436324.1","_nodeVersion":"12.16.1","_npmVersion":"lerna/3.20.2/node@v12.16.1+x64 (linux)","dist":{"integrity":"sha512-OoNOrsn06gy67sQ/UnOb3AeL5v6qB+jiQ0cj4jHKSX2tEOkonfOq1yVfTBdFFwCpqBbmXuAk/y6UzKjjICzoZQ==","shasum":"1e555b476f516eef2810e6359e033219a629e60c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1585436324.1.tgz","fileCount":13,"unpackedSize":25803,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJef9axCRA9TVsSAnZWagAAOVUP/ignIr8hgWk27D9+MCGW\n5Kr9DiJJZZY2TO+RteuS6gZWlSCjgFvoKKYIP7C1u5Zk7Ct5d8d1Qlst12zS\ni8YhcRhYRpRdZsHmTvTh2I39RkF2R6ldbyJ01q4BxqHtESjvL4Zg5yNdmlxj\nXt7pQiI2yDCa5B5D3n0aJynML2azVck/M0q9lOeG5bMvNx9n2bSSyCQEIMfY\nqAE7CiTLd4ujdnutId9hhLBRe2vnFGT53QfkAOEYUJEvm6rNtjrNyRaOU+v1\nY6d0B4XqWB9yeDG5pfRQHQ4s/Zi0GD4787F2WkWadCrDslgSGZVr34mbPHW4\nmyJG3nBGka0w2/E5P7qdL4M8+0jAks+gctMRjHIq5KFZ7yLLApp614O7lz7g\nG0udz/csRaMs4mmZeqjLjpwu5eTOTJZ70IY93BsaMWnSFAo/dlsB0kbpEkAj\n5ehZmA/pkjt7HoLnhj/5WkKILQSBKLhs9PJy5KvbytuaUl5aZs4zyqi7hIcH\ne5bF6I8IBs5BGbUrU/1ZlYW/xox599DlBAQL2jxmtizS3AmQDGIhHBbmIjsF\nmGnuUmqREAyKFCuqy9dq/4Zf3eJdq/GSTha7V2XfBkwZVDfw2ptmH9utTQxu\nYcIjIpjPPqxdalXaa2juiH7tzkG5iUeR7fG5WEIOJIN0JIp2hV79gwlEcFsC\nNeBp\r\n=1Eup\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHIo9y+czxlre/kmVjgby2g4BlJMHl0hrV834Tn6bCHtAiEA471IG/pqq+9AaGB0TW0bZrxCkC4rYcre4ZUsmwxbTeg="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1585436324.1_1585436336554_0.6943762513673495"},"_hasShrinkwrap":false},"1.0.0-canary1587943544.2":{"name":"plumier","version":"1.0.0-canary1587943544.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1587943544.2+47d4413","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"47d44132a9fef122b70f46d9084bff3d0ecc72b3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1587943544.2","_nodeVersion":"12.16.2","_npmVersion":"lerna/3.20.2/node@v12.16.2+x64 (linux)","dist":{"integrity":"sha512-yfJsSI9v7au8dQ7UY9/cPqXY1MOw8TonIjNrS7CX36S28V2dU9La1xb6zIo0r+imC7yM/rCTfg3CoOUjQP5saw==","shasum":"607e61a0187c9726191cb5876ef7506984cd7de0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1587943544.2.tgz","fileCount":13,"unpackedSize":25803,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJephiGCRA9TVsSAnZWagAAwPUQAJ+a1dc3sAKQmuG9QcRl\nRtwzFmuIY6s2iQXyJugXZsqCkQWo/3IigNZ7ur+p+p53ulp0yiedLX7YI8eV\nnYPu4Rv6EtfLp3LtTgFf3Pw4xNsUArZEJJiP2VKKIwNRJbOCeMoX/xmcK0bA\nDU3NgBUvGIaZHRBrJdzuzKs2ZhCjGryfBlD3ssKD2o5ufdWUNjjetyn0HFWE\nM3QQnirKF4hjPU1919nkCqsf1lAmUF1re2ZOA8rP1EOckpGwuxM1+OQCmm9C\nuoNUlaPNll3d0NqvgBZcVDtCBh+4LpDDEybrn0Tr0oSazbNO2+Oybm8bqyKt\noJxd1g2ViJo3ci4OuxzHLzR8eCFkEPl1USo4kkddxGejxDAPcEcQYHvhM4bL\nBG8D31YDO6xqHhRIe/I46kVCoXbfI9PBYY2plXmKCS06I0j0XPgV3looumoN\n/bqhID6uR83WIvK5uUGSAbKAwIASGSbZ3AOe0gp6p6efNl8NCp2xOFOKRLP/\n3HHH998pUvgPVx4YkKTicLAi2uqhMCOODDRwBJNBM/A5//KTLWE2INnk/DFV\nE9u+M3qEKmoylvpmuA9VkkQIfKohyrXqYyhnXYfgr/UR+pWMxKmaLiwnFXOQ\n3s47v97luL/Xmueyeb8diZX/3ANS31FD6hzDjuz7MMOEkFBTRIil74LE2PpT\n5N4g\r\n=Pi7d\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH+I6fkM9tOmzNSCY9LZMKTFn3xjdOXyJ+qTOnr9VUi/AiEA/Y/4TRyLz8C416JL60x3YQMCdNBaoNs14vF7w3+j/kQ="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1587943544.2_1587943557736_0.9211970626103096"},"_hasShrinkwrap":false},"1.0.0-canary1587968502.3":{"name":"plumier","version":"1.0.0-canary1587968502.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1587968502.3+1ad53e5","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1ad53e56e57d3d50e571251df5479f6b5c568866","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1587968502.3","_nodeVersion":"12.16.2","_npmVersion":"lerna/3.20.2/node@v12.16.2+x64 (linux)","dist":{"integrity":"sha512-jAs4GfWIitlnnFxfitCnkIaF9USxNIsILdUceejsba+RuWdy3Tvay/svwQl+jnXFt0bKWv3miFPCDeHJT2gr+g==","shasum":"c972bcb9fc918d39f8eadfab5d1047a7dbfd6ba1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1587968502.3.tgz","fileCount":13,"unpackedSize":25803,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJepnoECRA9TVsSAnZWagAAcREQAIaPHyWVUsxaz8L/MDyk\nznjD2OOu8WzcGN22eNqprsW2YoSwZ3iXp5TIvuqQhAM9z1CVyASPaVTcQHHf\nQ5MmstcxSFdck+KCjrozU+RfWTfjLUYsfytP5Si5qS7gOCw+3LukawtUzMrW\ngm5tW08xjuVnkynHOb+yN84XvBqf3H/na/fp0OViQUqihU20mhtJUSmTxDjW\nrqYoWVMuTSKjwEIaoKqnHkjB7sTFyOmk7NyYtrz21jFAYhzmd1ccP/n764D/\nUswW7nrI7LpISrj3m+RCi3KabtqiNN4STUegaQnEDLs6cHRhXNy1ncAbL668\nq+6c3rYywgbWVDAiMBbQAJZ1et2+/emZcTjcCdg4TjDMLKNo8UXvfNOWNP9y\nAL4O5xrEKgPBRU94H4zOWfyy92hlJe6TxRQfMTy02czZAHu429LwEbcS0/sM\nMlLSJOoY4V89m4UXFM0J2USgqBxEbODGl/iLl3G8DnQtfVUHFAdCmGAfzLeR\nLTjbRPbuJuqW23mnWDfMiWQ4XPtRF20+Bmxm+ywBpdnfhOuVYNxIFofunMJ4\n3MX8JU2x2+z83hlNDEyfzHz4dMJH0P5mW+4ar37lEWZrTH/UN/yxHQVoxA+Z\n1VXcTMHPTijcljFdw3m9buMNKwE4V6NrPuUmcz15Hf6I71d+e44GDA+WWdlk\n0F7F\r\n=PUrh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBomI/CmHRZfrxczq5pvLqrhqb0UifJmmdLflTiJmi1kAiBIfm/ne3LjF9Mr0NBCLSZB6s9zTguyxrsRIh3KPb9wnQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1587968502.3_1587968515622_0.8883438316821071"},"_hasShrinkwrap":false},"1.0.0-canary1587970552.4":{"name":"plumier","version":"1.0.0-canary1587970552.4","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1587970552.4+973544c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"973544cd6a4c991a122bbb56877123ca80f3c9a5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1587970552.4","_nodeVersion":"12.16.2","_npmVersion":"lerna/3.20.2/node@v12.16.2+x64 (linux)","dist":{"integrity":"sha512-PxBiruRqyM1uwnJ9ulMNKnnZEGTofT2vNAlFzp5INT9bbwOfzQsgvdJhXp9z7580TmK80iFGqSyv4+JLHs+5JA==","shasum":"3d61ea132877cd5697446786e45467a236de38c5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1587970552.4.tgz","fileCount":13,"unpackedSize":25803,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJepoIFCRA9TVsSAnZWagAAff4P/1Xi+1VqiZnlmpkH/1VZ\nLsvbgfexypHNTjcrqs0672HGahw43zPnjg8KGRZaf6/p6u4KW3K6y8b/UrmL\n1/2yS6Or3hz0y1haJpkTyMdxz43Lyq+q7p0KPljp6q9BI+pRnp/dscShEoVp\n+YBKQIK9Nu8qnAnUX67ECxeWcqf8skyRwUAS0AJuYTlquN7hvRqsGZIn8HZj\nBq/Y1AJtwNDHSt4/tW2gAlNK2fQYGJh5dzkfEiKzm7ZHsZ9IDKX5RRBqi10f\nPJWrLXwtdNkXfx6D2vJ5YebGfmm5tnS9lbxoTosuQvi+x20fKrEryhW1rz7k\n/hALTBOqm6X4kM3g3n7UBYUSeuXJdFtvEgAAk4S0wU65/wRIu9wHtVIxCYUT\nlR8mwBs6V+JzEoChkokHtVx+ze8vQtmMxUEN7chIb3cExxYRcSGTk5jMPk+K\n5kzMvFp0OkTAODKZtfGTvvYJv4H/PbCFf8ZJtnTO4UGAf/jE92PG5XuuxKG+\nicmj94QnJPZasoSGQJZaUNVLZ9UyRAOk8pgYdy7x2585W+EqZlKSha5GFyDv\nJCVBUlg1U/igosjeMVvhm+kxFXoc0ORe+2wQRfVgM4w0WNn6p434ppIs7nB6\nfHW1G5M/E63BOa7HrZg1ShqI8QzmKEZquKn2zJRCjpI6ODQhmly02ClCTRXN\nPAFG\r\n=1Ht7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEup17/aiknSGHR9dcy7czqcb/1dv/XUYwAWltRIVZ0LAiEAnaSaKWzEGvVVoN6B5ti8ueWRTbNErjewSaZsIixotlY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1587970552.4_1587970564613_0.7528659234067974"},"_hasShrinkwrap":false},"1.0.0-canary1587980512.5":{"name":"plumier","version":"1.0.0-canary1587980512.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1587980512.5+bcadc46","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bcadc46d869c92f7863a48c2652338865ff44323","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://travis-ci.org/plumier/plumier.svg?branch=master)](https://travis-ci.org/plumier/plumier)\n[![Build status](https://ci.appveyor.com/api/projects/status/6carp7h4q50v4pj6?svg=true)](https://ci.appveyor.com/project/ktutnik/plumier-isghw)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1587980512.5","_nodeVersion":"12.16.2","_npmVersion":"lerna/3.20.2/node@v12.16.2+x64 (linux)","dist":{"integrity":"sha512-tijkagQk7Sa/PvdHdJWurWn9jtaqx4bqB1MCDJPirnKpYy5UY0dts/4OZ0ucy8AzTj7GtwH67H/Rm6qkhu1mLA==","shasum":"3180463dcd10e87a2be9d10654193082e6d158d2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1587980512.5.tgz","fileCount":13,"unpackedSize":25803,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJepqjsCRA9TVsSAnZWagAAdxsP/1jrfYrqLJuSsm216iNS\nNQ0VhAL1ALX8s0Edpw5lowf/0QQNbEM2BshFI6J+mYi15x39h6UUNcCKfX8G\nnNtE7xARDJEYrhV0D30oNRRPOEdljOhd5eR+IpAO5xWmLGpmXr0TLDAhKQRQ\nlrFySRXeDa9WdAC1eoAa98P4CLkRH9P/VMOCyCslIhCCR+hPHFm+ARR6Gzk2\n8PwqkKYN59MC0KK5LmpT13hrScroSgsr9vz3+VDHZS5J3YHya8Ss7E6dlD1L\nc7MOZurSehTMZfbqq1Dr6xfBk93eEruXyMG/LGh/DnnrqA6dBki6U3KFtcMv\nye2zxUEGPeuNRrXFt5xwoCBFvNCe0SYMzNw9ugqVoJPIsndDSOquG3qe+3ZK\niffQ1nSco+9H1hW8VFNq94ZZeJkG6vUyb7k9PADsvFTtKhXOvaweYhIqbuFU\n5uPPzjc8LJnJrHUx4i/HTEQ4BKg7b11FLgPZ8Nc5rotIOiNH0sa2hdvH9Ifs\nqU3aYqv43bu0BkRQ5Bdv7kmrocbOD2Mt8FHtD8vhVXfC6+/nSjYNrBDmeKC8\n5Pubcufcay5p8Xujd6dt3MsLWf49Ez8q094smDC97cbZViioeet5kBuzMPrS\nBlNIzJXDA+4by4kZBh9vlNqzmVSSCEw742s8jj4DI1JGU+fr+Baty4OdUo3P\ny+t0\r\n=1+Md\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCS7A7ctCN9IA0S5va3/YFeqGDcWl8/ncfHU2Jzf0AvowIhALXZkQXV3tlZmBOcbmu7dB9PajLbfUkRneTO7Tc9GZdZ"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1587980512.5_1587980523591_0.6251999106157184"},"_hasShrinkwrap":false},"1.0.0-canary1587984926.6":{"name":"plumier","version":"1.0.0-canary1587984926.6","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1587984926.6+9b96afc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9b96afc2c80ef732f9887f531202a85ae514bb2f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1587984926.6","_nodeVersion":"12.16.2","_npmVersion":"lerna/3.20.2/node@v12.16.2+x64 (linux)","dist":{"integrity":"sha512-2BZAmMStRUzbJw6B1Ma7O9N9OKHMLJWqMaNstdDxycahm4DNDtaTAGYN8kqKiGkVnNKVKJf7Dbkxzx0srkkx/Q==","shasum":"2de1abe1b397261856359ab1811dfdbb45a1bfcf","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1587984926.6.tgz","fileCount":13,"unpackedSize":25845,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeproqCRA9TVsSAnZWagAAdHAP/1ZEG4rjpBDKodnEUdY/\nAUkqgNrGS2P34M3/Yz4o5Rpk6MdAUtT1/zPj2suLflxfqk9SATY5kMuGBxRV\nwsi7ig4wByzHWYWX1B5+s5eVPll2avHGwpg4V8r5Y5W3skMR+OawvQNRk5lD\nlF+j2hbP3q5YjHA+T1qTNliqpxPW5Tu7i02/thhuT0Se+Et25pmYEXhaT0iE\ntD6FtNyHbn0o/GtmrcthOaAnZtfMbhKg1UoKztEB4MckxtzZVFBwQmgSPDny\nNcFbSCZigsL6sphCrB2A5UHwgxl9uvL72SE+axY6I2je2h+ppEnPE6UM1Q3o\nUAvrh5KmoiyqGyyZrGYi+JxafB9rdRIvneEpUFwva5c1oXi9cI9yQxw2M40t\nE74KdQbzZPh24rfoRaT04ywtLHLCYzlZUBUgfRRg+EzRfMhjKRZbyxf8ZuGg\nHIRC79jAgXfebscWfxysCH5rIWxT7Vxfg+BzCGQVvN6qbFBOhYDnqMNo4sWQ\n7b6XNHWLoBlIM0CQCFhKjTFUozsYYdiHUlf2F59cS471Ut0V3u1xoYCIlRST\nA2EWyKqY/lCtZ6lUF75ZszX7w5dpAuCnMWwix4hBUcPsgRa2tSXbFmCBldBH\nOknvad2mMcbA3NMnjcHK56C+WW3Z4l2u4ELoFnejfKIBpGuaq8h/Sey9XQS5\nE7zE\r\n=PYug\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDFmqbmDhQwvbd44Gfdx7Zd82WMzByQSaRWypo0V1/WUAiASHEYD6ACUVLNIbhvnwrfm6agWHzcvSsQdrPPgrks9Nw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1587984926.6_1587984938176_0.7368175054825832"},"_hasShrinkwrap":false},"1.0.0-canary1587988578.7":{"name":"plumier","version":"1.0.0-canary1587988578.7","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1587988578.7+c1104e8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c1104e818015f6023759645554846c544427af7f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1587988578.7","_nodeVersion":"12.16.2","_npmVersion":"lerna/3.20.2/node@v12.16.2+x64 (linux)","dist":{"integrity":"sha512-G2eEQgV+dvUOoAIaAYWYbB4FkbCAPNXej5WQqmCy4hG/p8BPCI3u6uRbpUl/Eh8DD1AAnBcdRuksEpMft7W2UQ==","shasum":"0cd3d97e00b14c846bdf4c719f3de0c94bdfc73d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1587988578.7.tgz","fileCount":13,"unpackedSize":25845,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJepshrCRA9TVsSAnZWagAAUbMQAKKc110ccFIY+aIWw8RM\niXvAIw2kxzLZXOynzfECl0fVqPaMG9MIAMSU+NFuszcFfKnd1bn615i+zMsT\nTe2eGltwlXUASdbYTd4zrYJl/huz6Gu94UTeBkgwK3uCsZIoY/YR13GaM5eI\n0+hGasvOpCOeBOY853pGg+w5aLK0EC/Ss1n1FBwSFI3EXCxIHGdBP9ui12jN\n/mWbnf29BFLiGUYRcSclbwTO1KYcHez5pVXV5lcKfWhn/snVXsw79TPcOJ6X\n0KD8an9whDrH40rkGVRx2Sm8M5QcS8SbkdVv6Pzu768hBoL6P5fmz5coEhIO\npa62MtaOKcWmbIhzJKMBMjgi6aF6JPwYEfi2GNpzP7hyQK/p7TGTqD7X62dl\nprMiho9GNjqY2gkJ3diqt9dmB1vr84e55dYdHI9JFhb4I8m95hK63LuYrD5D\nRxDjlI3a54ztchrV+QiKcAnXsx0JfkrGU9Y0CjAkZ54qvwat2zR5NrWCvM42\n+OK1oZ3nhWSDl1K7nM/XTqqDRD4pr2RmPXQB4O29ggCDzryxwwbxk6HM1Boj\n32yavISU1dt+ZtkYXFtSC5C4P+RG4Ln6VuzWk0XgKpyn1StzOJDIEvDo5Drj\ne/5EN4d5qAdHKIIuyawHV2MjwPFdGl9o/g3rpTgYgKSrP6HXT89H9caquwpA\n8RA1\r\n=YmNk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDuy8NmI65haGt1wkjoCbLNDpVZ/cKyNyXG9/sv2eHSvgIgD+RI2Va2VQN7MP7I38tWWwAbKZpEZYdcGHVYy4ynM6I="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1587988578.7_1587988587520_0.6717239094107861"},"_hasShrinkwrap":false},"1.0.0-canary1588205336.8":{"name":"plumier","version":"1.0.0-canary1588205336.8","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588205336.8+ff0044d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ff0044d2166fa84507d7d295862f240244c032bb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588205336.8","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-Rl7lJzJVvBHV35JjIMC1QcYexkoFAbO9FsIs5dJ1RGHsdye0QOKryTsUl+6N4PQ/SB0k9v5qix6EByDxxrFRGA==","shasum":"16850502931dd5029f6089e806bec036aabccd2d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588205336.8.tgz","fileCount":13,"unpackedSize":25845,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeqhckCRA9TVsSAnZWagAAv1kP/iceT3syXbsv4xgB3dQU\nZZxec75jObjZzn6BBLSFDCwUS+56Ej8YQCeas416H93QLHiY9mM/cF9ZUua9\nhY2+DGflkb2pzI1S4cv3p8uhZCCMk5m06/vceBvOAABsTJQJC7qpsnFssLr1\nw2TOwqG7pxD+zOtTnmgS+LboYJ/bmvOoy638mwDPI73zRWfA5yLvmP1zOJ/Q\nhM13Xgtnb5+3v93652XD3GVXJv30N0dX4BsKORN9tN5ysCsN1ZFWawlOrPcm\ny/EXW8Ng6GxCset3eTBliwJdhIUVi8t7W6x/BWHF0W3IEdhJx2114O94jHaf\niqOcZ6MJCP0zZ5OYIEKvB3lyLK3+cHfkTUTMf6aeheyWSKX8+E7tLeNAPbUj\nsa9K4dOrk1sPXCS7gc0aTkImawXZU8v2Zj4Rif+ustvqE3PivFriD4l5RhQs\nrcKoBnAxomdFK0dabpYB2aIIoFLqlEpkGVsS9jtQSV/3D+Y3jHM52fu4ld8b\nAZz0Vrsi0iwhNGRuUR1OrUlXq+Rhkm0IuWgONo94jf0AGJFvBAdFTbJVa5Wb\n/3Z5ZJQCrMdXW/gSirgIIKJUDIczk4iPO5tRjaWk4F2fVGLuWMWnqvaCU/K1\n6pn4iiXhaGIJScpVwZKld1CQ1nQfb9D7LmWrs/cojPeH94O/ZonDew7AwG+x\nsRAa\r\n=TGEq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCO/8kP9bvfMPW4cnwCKfXBYRtzxoaDuUaA9D6sQVxHdQIhAI6C8/6iZeWKb1jPLsHa0p/fQ1q1xQVEW8obh20xATvg"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588205336.8_1588205347818_0.8667851868037757"},"_hasShrinkwrap":false},"1.0.0-canary1588541260.9":{"name":"plumier","version":"1.0.0-canary1588541260.9","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588541260.9+ff8fa18","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ff8fa187e5dc6d9d3744cef01f86d839b60bf3da","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588541260.9","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-/kviBGb2YtJ8alTuKc2Y1+xTawlBERAiFDuENlRwHlF9kL+iCNPQhLATgG4K2tH7NnWtx61p5f0fjSF1eoNrHA==","shasum":"def4b3b921c15580e90d4d1d55edc8a967d9dc99","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588541260.9.tgz","fileCount":13,"unpackedSize":25845,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJerzdXCRA9TVsSAnZWagAABVMP/2O0L8AxwWU+SlqBSZbp\nSh0RECOqY8O7qKGlo+718YvGU5MmBlIHNcf84+JN6TTG+u9adfRN1uTYkmle\nXOuOsIUpQGDkzKj9wPpxgiuPgTN1rQOpdKBFA8BXjd1ASs1CQoRlREld/LWF\nxlafCaji3yDYjjUWtA4WG0EHa56t+F9SkJqWdGM3KHTQgOulan2x9olXz0D9\nl8cxaqbKcT1TIZqN3NFLadT/hHLatm7wBfvb+MTY8b8bnjId1uKB9LiBTY0R\n4SF8nEA6HNq+VJttDrh7sxAHpURlv83ip2NL2tS+q86u6mZF3io9Ax70bgWy\nGyppjJJ3yRVRH7ZC/3+1TLJ0nDet1p4aT7P4OUT0mlWiX4yJQuEENzu0j18o\n7R2mzm6GjRULNp2VpOaeKXcy4GGSCP9jlCUqumQH5bkRzTtUGMaPgM/QgYto\nNSOTu0BEaiXyijPPhIEeipRAEqhU9TnTH7DaQTNBT81W277mZSy21ic7sT9v\nADc06yFVyaWsO8Lh9zoMFXJZz96ZJs4+YFb6PJEn6QJYCJW/PlmhVF9iA8rB\ny89KaLdbSEIcgvIQHYKJoxyYZyiXPoq9KHFC4fR2A1LmNFnWVgBuz26k0Tal\nTh/E3dG6tnxKHRXFF6dROZ1443aNcygUYDXsltKTjEo8ayCnfqa1U4lnCJ63\nDYAd\r\n=gmz6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDFqv2ry8sJ2oCulbu6W/FgKDzPY2dWTaOwlVEXUePRywIhAIrOHBE7gma5Ao4jk/6BwDfckywUYmrPhB8wWYQYz/cW"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588541260.9_1588541271539_0.6393500282704767"},"_hasShrinkwrap":false},"1.0.0-canary1588548530.10":{"name":"plumier","version":"1.0.0-canary1588548530.10","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588548530.10+22dd39f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"22dd39f71256edb727eb4279207c986e2d267c5a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588548530.10","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-wXighunivjX9j3crEJjrM3+XU4qxcI78iq4cI7OHVhmiT5G9vRmw85zD2E6dV4E/J1C+tLpTqcCeOVuqAkc8rA==","shasum":"ff3879c838b0cdb4b604be9f12c2d7b4b2a0d674","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588548530.10.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJer1PCCRA9TVsSAnZWagAASaYP/3DAhzyt/6c98Tcgmq/U\nccMJDjASCARA0Eh9HZloE6/v7fmkP4g2OOsFPmzYFJHDST9Zu8pAKHGowY8S\nEE3mPaWevoNHyK1NEMOUUefctW+Du0KVeu0wKvpwMAntcrmL11/I/GAE/Fte\nJnQDeF1TAe519Wlw4VOTEaWTTFnU2+ZXwzP34kQkfZFM7eEXhmKuucLgyjMQ\niIJzeZKHEuo+i3li8KBquQgfBxFkzPq4lwD4caADIKQHD+pDIVJTeRiHwlfo\nQeY/5McJb+mPtBNI9bYLInWmqGaaeUwqzYGOu0NaeJwb+N0pDYS2cMPj2Ks2\nPzdAe6VN5DHKpBDWT/19E/NHiX54h89htT1otV6bImQIGki2AYnlKLhrui7r\nu4/eo78a9m82zzC/YOiBvztog7mSuJ9L+YJxK+hK0eMUmBjHWr5wCKFEPnrK\npz0cq6qxCiaLXG3TGLJIBKr09OjnkZrpAdxSg/2/C+hnF6vGCg/+I5D/6O9q\nX/HyBP62Eyl8muQLrKLv1HiAhuOkgay/tu4hxnfNRaG+F0dNfZlBkEv3YjVl\nxmiAqTZJbOauX+vwx8E/t6K33m0iu4pvfJqTHaMh4X/wr9zRRYZXwQt4Gfgu\nmQr7xWg7AFRIuUWByyhqQNVvseZ7N2QfixlHBPWtSEQwKdnCRDJ/AP+7EjD9\ne2CG\r\n=qScl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICOW36UeTNo1n7svR3xmUtVEEClxhSZL2sBiJ8w0ZVooAiEAwo6p4xaTwfgXB/9UIdps/bPaMf1Rft3uCa3iEhJ6L+8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588548530.10_1588548546071_0.5869859701415412"},"_hasShrinkwrap":false},"1.0.0-canary1588575549.11":{"name":"plumier","version":"1.0.0-canary1588575549.11","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588575549.11+7e902b5","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7e902b5c3510b7219fc9eda687118cf85cee8fdd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588575549.11","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-6xZRClllrBCzixiWCKHQ8LgVwQLdj85OR2sZZrBPEwL0H7Peh/gw0Uayhjc5i4nLyDzyfHddd0t8zcISC1ghKA==","shasum":"7e0d387e35d870caaf49cc8fb0a8ff328bac52a9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588575549.11.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJer71ICRA9TVsSAnZWagAAgSkP/iO/XaucNjwvmhV6+CKw\nY+hOgwnYEEkUk3tNb8lIZiFfIr7+es60j//KRQmoqeuzsgy1dou4PBI+Oe8S\nu6naLNor+jpIQu9lQtPozEfPh0qPuJXfcAz+0rtMncTFxCwhbQeEXn3ATM0Y\n9j3Zg/YUKGHmGR1Sv7cv1Wj6FScTLL58Yc9BqbymYaYiLElY2gtMt1Uau0h0\n+dbLdseOH3C+bnIYKJ6hhorS4vZXDY9doMvCoJMgUZIwhRKnlo9ETAQZHTGG\nJ/KLcJ9TRmkf/OYmKnsZZtuRuVFW5GIRAo95Lk8EMdBkYwGTV8C6sNSlLqEs\n92DYGevTbLYGc56xVqoDxWa6/JGGdrJCSnjJwAjXci8yiiPWCvkmAiF4bGoz\ncBlbvLh0D6Sjw+MCRBWm35wBpzC2ZMs48FBAwN72GPMthIAlfZKZwnZkLvov\nyashQ2/OfWyyNhpbAKEftSCZToVJ9stwjEiToF2eqksUuXEy1O7UyqPZKTTu\nNwn+gUNhadh2KwCFIPqs2ZDdO5Y5pF0sAw5vPlfuFvsGTuvCf0JlakmTvO0q\nJzzFcDLxKJI1mniwlvlWUrI3t63gPYzGHXndlwoLHl2c0Nfs7mzFwLGVr1Sb\nH4wtqKx5uQnvc+1KmZD3ynnHgF5fUcCDzQdot8wOr/wuoqA0w8qkRQA8HmPf\nQuss\r\n=M7w1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCEq6l0bp7Bs2R6mDD3qlDMFiDkDEAtNyZr1XngOdNcOAIgHnhiUTQY53E1W4Hba/XEkLcYGEP688KU/j5kg3ZAuGA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588575549.11_1588575560357_0.22758816848322527"},"_hasShrinkwrap":false},"1.0.0-canary1588674233.12":{"name":"plumier","version":"1.0.0-canary1588674233.12","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588674233.12+cca5527","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cca5527c342f361c4676015f89aab1ca0e6dacb8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588674233.12","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-fyroqs0NnJUf/cj6KiFooEx21t0l87R00p8X8lI76ZO/13B0zTY8xlfi4opRcuA4JXC8980eQe2JB+35PbdRrQ==","shasum":"610bbff8d146bf6f77cbeb8442140198f04adb3a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588674233.12.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJesT7ECRA9TVsSAnZWagAALcgP/RF57KXceOKsns+2oj/j\n5aJhiUdZZYd8f89rkPYztdcijDYoZdyTW18l3CLLBxIj6+IqDYVdMG5mUxTg\ndtEq57u/3CCKjqYhOBk9dgeRAm2Rf2o6oa/peBPxjXZHLQ/YCnwucLIfrUXx\nZ2+HDdZyKP4IMMsnQhAJV7zt9d0q7ZAVxG0IBUbsjy/Xv+PhvsmxWvlGKtAf\nxs4Sup7fpbOfa5C91SDZAi8WHsl3mel9SZZZV0pWr7TaTLSJjD89gBYFORe6\n/w8MdhgqE/Ey2H9cu0NNs+Kmanfp4yxBxDFOPLwoOQpaJXoM/NlFxnB2ekML\n7d5DlzDwjSanUFHnoWu/MGGcmqaFJFozZjb5crOwiUgx1y5/3p1bzR2JbLlG\nUnCukxGLx6zwGmjrIJFYqCVoorQwwUWtPWVNqo28X4nXZuYy5Bi8qMuBHGQ9\nCUJzsbwzH91dmKiTXfZ1oNVpDkVqzzUZZO8JsnQnM9tm4X3HI2Ja5X0h1OLs\nWj8yJ0XBXsnBSkg1nKm/Z955PTkNfb8+Um+n6/TWiVq6psSRTKiaD/BTNhzB\nQdq8gzQ1ytEJ6Y96T6AO9WQ1uCgGWU1ceFxTduB40/eCkiN4buPLATLGQUnI\nvZcyHEiZ+q6CTjBk/xcS6oocCqbKDI7VOU4AfxZlHT4Hw8cT6LnSnfmiIGiJ\nS+IC\r\n=zIBX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZJl1t4DSlLycSJwCxwMOvXRqtjqHYbLBlUrgx8M/ZlwIhAOJONmuDHWonYKsbtWOBGLNkvsIoARhGs3vqbNwM3xDJ"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588674233.12_1588674244467_0.46433623556594394"},"_hasShrinkwrap":false},"1.0.0-canary1588915726.14":{"name":"plumier","version":"1.0.0-canary1588915726.14","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588915726.14+330c5af","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"330c5af77de46f30217e3cbfeb1e7900d901e3dd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588915726.14","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-/WJNtYa+ErITfaxDKdjU1yLkql6y7trMmgLAUMblVFID2cbXs4YCsQvTukx680f4t1M+l+DFxrWqZaN+EskKfg==","shasum":"00f4ca0bb8077cd52cf2b805ea307c127dc19c43","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588915726.14.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJetO4YCRA9TVsSAnZWagAAsD0P/2HqVgAMVrjvvGTp3GrJ\ng+CHFRoDkjs2sZwBU0/Pgy4ELil0pKOErbFkIe1AnQo3W8Sx1lwsSw0QU0q0\nKrE/pxTk+uxLh0mLb9vIOrXd5VT0jZ0GjKxL1173dsz1U1aXysvI8YP7+lah\nXMGk8r4XgJWVF7k9F+OPtUlYOSV+6HaAadRDz6d3KlaAefMeoFr+5wyrpwS9\nZQY2doqAVs+qNa5kd0L3fr9jmYzzCo6/hSDf7QFChXG/diU4ybgmPWvMiS3U\nQzpL+Cl2EcE5wKMfnK9IDtBuK9VbY3NprByC+84rSR77CDIVcZfVmtp7StKi\n7G33PR1+kBXgNDj0lp363TZX042eUJQ2dSh8bvuh1NEwEmn7mypMWg1tXerX\nDYWr4TW+kSMddRAKwqNq8d692iKaaflZlU8yFnjPjQ2OOyD5ALvDPJPjz1vr\naSSm5Xrx+wSgOq4dUo4DWe8PPt5evRjCLJVlNUiip8GcOHuLfxaqZhSI5Tkn\nO8Puk3JFMOhALQCz4hu0qg1UTv+FavmL+6g80gDZGmScJVISA4oLmgLgvMrV\nMne52iUyMi6T0WTuQEbTXYPaY2TmecA2jxmzYB0iXYi4UBXvr1o5FZUuZzGg\nqqdgDttp1FBtZg2PEULTyET7pqp0cZV1FWOy8Hh/WXrEByhc78mI+qBLaFgX\nHQa7\r\n=wTh4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCv2P8QfrfRkSxRwaRQY2MgyRaugw+z4lQ+Jop3eCWPdwIhANQW6wY8KboeCGr1FLquats/ppURryna+a639s1n0Ahr"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588915726.14_1588915736059_0.12103235217562536"},"_hasShrinkwrap":false},"1.0.0-canary1588938574.15":{"name":"plumier","version":"1.0.0-canary1588938574.15","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588938574.15+1294927","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1294927a1892b65044a7e707cc7742b84a4394f0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588938574.15","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-2r4gzhheNDKD3knXQ9OGnqnLU3JHVl+gp6i8WZHM1VjmIR3ui2JUOVpD9PXq3eeP83nk/ZxcUA8HUQf/xARc/Q==","shasum":"beb386ba38dd990fd216a4c6a7031c4bbc48ba6a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588938574.15.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJetUdaCRA9TVsSAnZWagAA91gP/RQQJ3RxLZQK52/Rfyph\niMDHCh3MKFSQYZKrwJ+r3L3vKxypwUrrWA+rZFtcnDSu0EbRo/GP1oZ9UEdB\nAKsEGkuI/EsrIOt8dZivIiifqVRHFVtC+gTU6TUl0EJLhy4mL3pUGNh1ya6F\nyuyXNYiiX6KWkCOAeTstO9VvHH/qwGdTT0ZLRSz7CYUalWaVwP/+OnRgmut/\nU+teHGrsb7OSYER1twBQ4Y+MNeMQKzZ+BWDLb8DEFu/RJ9nUqtPkED8av6QF\nCgU+wzcy7QzKMXAIae4XsZHvRIqWkApeEl+buvFlkVI52f7rqL1BlmqrLAw8\nUvyW8YcGR5rpaUvz9i6sr15kuyrheTXMmR2iQPf8jhB0gDc48AOMpjCjq1Dy\nAtMKgqYqkQIS1JSRKei3IAPfnUPgAudOQX9gRqBSguXSF7CJDFFOHoxQGouc\nyqzfcPjodbUBIFrrkwtfcTWfQHaYMlVb/4RGeM/oHnfHJLyK57SOsvTLhD7c\nXbuVCo4ob6s94TETltRcT/JTDIrVYmGB5LLXT7Gxt3LHdhfaIplQHNPtxbbs\nNt1JtLg6EdtJ94JeLL5BDg0uSmAx5EbENAq9PKOOHAyF0Afkfj4uaJUMnAcQ\nkDgxp7ZkarWhqKi4oU/s/kbrzSGk7mY4LSPzmUqAiMn6j97CxLtcUSiUnA9z\nkxHp\r\n=eyms\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCvMaqOrsLoq9ytl46qWZJO1tZXAQYOt7Ci2sW+JJgfUQIgT3I9WsiYRGdIcKZq201oE1mEGADdBakn5ZQmlkYeqXs="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588938574.15_1588938586538_0.8179857659474594"},"_hasShrinkwrap":false},"1.0.0-canary1588940525.16":{"name":"plumier","version":"1.0.0-canary1588940525.16","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588940525.16+39d9bf3","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"39d9bf3912f149b4f65ecad735b8ec778f4c313a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588940525.16","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-RWtk4/ju2IrNAPPUllm4xVv9wn0hyOa0vnxO0xlLRKNMCCTVRwh2mou2JqdR+Pgc97SqUFTo1yjkVlNrkRmvGg==","shasum":"788358b886bd2e9f0227c85f047ed9e0af30aad2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588940525.16.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJetU76CRA9TVsSAnZWagAABjAP/1wxR/FXspLJFVuAtjw2\nwVe1a3a76dfooX9L7KmdYM4aTI4HDGYOLNARETUBAI167VwLAKjM7I3/aqP/\n9LxhWGjd176rOz5weXsHhJJASVI8blwO3vfMvChvbn77tKpTsL9qcQAWc7pF\nxEawisaMYfDDtHF1KMV24v4onkbsetXitHcdqsq96PcWb79Q2cN/6VmguIi8\nf2IeQzfUxRix5ywKXVlbFYxD4ppQX4vUD+PZzobCJOLkYXPmg68sTkflVfAA\n/sUaUvoiRB0URq3y5v7KVGE4Wd8qAo3UFL6pjkP05W6FxDjnJvjl9LxBWDYS\n8rOUKR48Np91QAOijHXJlEwzSOMdHV7SjJrbrPUAKWkgwSXgPL6XgM0DbbUJ\n6PxhzyUbqSN72q8WE2g284U0/jTJz1Bv1dR1d53MypK7+ek8mScQNm9gL/H7\nNLt6hPosenf5iuewNWQI5ntZddA1E23QA4NjosRxvWCi0mHlCwQ0wcwuq7rX\nOHIPboOCv7zSWktNCx6Y+6GDzKPCsCy3Nv/bUXB510Xwt14FtG4V5pBSd5D2\nm/ot2aqZEPOzxJdkAzWVkF83p3VgCkeQcu3iXhwjYUAtfUwXMd+kVwS6x9Ky\nBfqUuHh6puxXewAPi3xW91pfh1e0NUOttdn4yb23Jt60mEbB+4KQI8XO7L4+\nrYkE\r\n=3VaQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEZVXKjVUXGeKBAMWXTqm3dKingLbibwZyEE2r4JPBY3AiAkT7O14KKMm/E5I1/GSgGdD/U+GX5NAHOqxIXqPpDobg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588940525.16_1588940538317_0.6304873699141118"},"_hasShrinkwrap":false},"1.0.0-canary1588968898.17":{"name":"plumier","version":"1.0.0-canary1588968898.17","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1588968898.17+010c2ff","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"010c2ff4eec986bb140761d175bf689b6dfd7551","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1588968898.17","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-KxtZmkpWt2uh9pYstr2ViAQqHkly3RpWvhFNDvF7KiJ46uJNMd4BYCXG8LzfecXMt9zIUkoBt3e2VeuJcKyUlA==","shasum":"eab63d0c5570b1473e96547b36dd8a2db3c5b6d8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1588968898.17.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJetb3OCRA9TVsSAnZWagAATIkP/23d2bsGSfHpRVyxbpn8\n+UbyDoxCaW2vrtpzuI2e0shSZLxjyheuEsTx3R3XSqmXbIAMvYNYaRgVMPC6\nBHTJfBg9MiET9MoJ5bUwGkxbH8T4BszT0rYYsclIkCbL43MkDtdt7dmZ/AAo\nEZtNYzprhczjHxn77Ubi5SnlfXH4gr4HTVF3TZQX3JuedKHgivg6oUq2skyh\noMTC0Lfr3Z3p6K+juhJX4rGE1NAv6MqPoZT6iyKOG3Drr3riE3e1sEU5DSUX\n08KDpIwk6TmjF+qkKB+xOphmByNRsC7WYDcEgtyoJkxqfAMAh9Hry2rQ36pR\nv6rN6dYYQuO8jTbhUWbiIPF0TGPQlDQbweR6nryMVq6xQBVWLzCCG7tUUNPa\ntadxOxWxsX+ZxF0LIH0lfFYC3hfTfUvsNa5tQUzEx0bg0vX3+X3rZpYzxMRU\nlr5wnwcpAnoIf0Nn9gzfYT8kXmnpX4LzrbhUvtotrD609V8vNQNyL3khXhqe\neuqw2mImDkXx6j1c4oPIQUPzZd6YAMydL5+OWj0O2+UnQTsDMKuYzcN2+YzG\nmnM9zsywQnGG19kztGlYyQfxfi3KtiPWHckQ7NU2IGdnFiXWGKUAF9Sto1aE\nDxiZFluy8uehc0WMzFUaHmXNGdnOqX5V5Vdw+w+CLU0k+BNvUYYkEWpG0YH7\nfqId\r\n=yzYW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG4RebzWcd4HzdBTZhEIoNcNTJi6CQGEYzUCEOhtbzBSAiEAimLUoueswu8BLMpVqO47z3Qr0truid9PnYUxLyNwvk0="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1588968898.17_1588968910076_0.36693659065907025"},"_hasShrinkwrap":false},"1.0.0-canary1589001356.18":{"name":"plumier","version":"1.0.0-canary1589001356.18","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589001356.18+e949a47","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e949a47edcf4db33ab3f6f1018f46f157a227fbb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589001356.18","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-V9JQWsg0EpdUv4cmvreLI6kbnh1rs0gpWbuBg2Wus96V6yteyR06cFNNc6GC98laURI0HAgDh+J1xqxyipy31g==","shasum":"eb947fda8e9351853a4c8a76e5f7687abe80f232","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589001356.18.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJetjyXCRA9TVsSAnZWagAASRUQAIZOdaPLOr2s3zzGOjU/\nXBqgCnRc1VcE+hwzGx7KXRPCeXoVfhcIDzUBZJVxy843JNQHfSQ47vhSfh+N\nX49gXsa1ClEYSkqFmsBWfyiPtsn6TTEJPtUXYdj67XwCOTaGh3mmdzMlqu79\nzkdzNGfx7urmzj9kdvHFyEdzLXzZqdfjnk3Jpnhrxg8l8btIfwURIax5hEI5\nl2ZxtHlnwTdsdlaER7HmxXgcyFSPxoTPEt2lH5GMXGHMHgQDsnJwJawFFsTu\n78oCdEAy1ddyOZS3naaWk6HkSg3CE7YxAcfsSPC59iunajxgDMj90B6L9onS\n1w2Mijy9bihujQHAyKPk/pHJFRTh10nmcR9aOobZOUV9vew6LC9e3MKJhEX+\nZB6g4yr1JcYCNjbmNKL/X+binQV+O+lhgtBXLVHWMCFVy78H6VTn/tastwIC\n+3jPvAc9KR5Z3Mi3zMyYvS9tM231HzoznGxKE00bna0Q3X0X1r1vK9m2yip3\nq2Sqc4bdfHU60WwArPLS6cmGpbtrGchHeGsRvYX3V67BHAT3Z3j1yTA3528F\nPVSd6n+P9+M1nR4o0sHybGEPW2o79C7531y5sVdAXvS5ueJ/IqSJEwBo54aL\nyLsvcJUJ0ZbRfKA01SKrahA8hT1PTyYRIRFJODKxCNO1FPM3YJ/XX1q4SgXw\n3ai1\r\n=vwqT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDAtjmBEME1C2ouLEyCgZ13QnyN6Rio4SqSdSL6AJooOgIgYGZQLvh+QIb1JXajXOSSmeSE+vTLGopFRM/XPg/+uYs="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589001356.18_1589001367357_0.266941291782711"},"_hasShrinkwrap":false},"1.0.0-canary1589078917.19":{"name":"plumier","version":"1.0.0-canary1589078917.19","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589078917.19+0fd32a6","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0fd32a67127a2af3b81402132014a9732b84ce94","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589078917.19","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-I9jc1ka9+kdHNV3hUbg/Zdq6d5d21/rgm+aH5HkUcplDisKuG9F5GjKTLmqaI73gf1twXrm1tapY8PKYnNbHGA==","shasum":"6cd5b618e53dd5fb329ea4adb6823ef7fd88baee","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589078917.19.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJet2uUCRA9TVsSAnZWagAAfmkP/2sMkZnWm9SGWQxrr/Xu\nDZ5uT6f8vMIpXixfClawKY+w5yJIsK1zTsGzbAfAunDob8Caj54/vHDDufcu\n+QqWpURPZ7IQj1uxiI4875R5Tvj3L0RlVeedlAWdDlnjCy5ZByAIpUOBezZb\n5/FVgxrIscRbdA48Kcoro6BB1gXxA8HGGtvR2SPqF1+/gpXHMGy72ftx+bMH\nDLKkB3bxtVSFVbLUV7NA+iHDwEFuyzAnLnpmngKUtqvwkhaIUSPLemG+v0XA\naqbi+VvJz7XYLHGWizjSe4B/IZ+Hj6ZjjDkdapry2fsW0KQQDU0w/IgblQR8\ntBGPxoDh8lUda/twOuj2pM8SHLwel3kBlQVsf5GW959ZshfzaJSJyGOcXC/7\ncwh2G5VXAuzmLqy2z4LNOYDHInA1uMbgKNW5FN7Ey5lkJJoV1kquGCG3YwGk\nlMi0GIcEObGN6FfWo2YNpVynaof5SaKTfbNjQ8zeUQ5IaaWwDA/lc1NgZSOc\nOUW6v4wvhDutjSUesD0Kd/BFtcTT/FiaiBIznLAeJ3mOeO2SR7eRGf8szbTh\nKFKutCsr1qHTQNgzkt+wRnsM4sEwnX0ejFtR+MfGwH6KsMKBDCTLCM75yUHC\nKhdUHk9KrKebEm+9xUYHlXwpu35oOitfV2gxdbnQwoVYb6Xp4sc/0WJhrqFc\nkJtN\r\n=/wI/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCUSnNeju/f1mVlGOPxmqiMFBcHbZtACrBZMGR4dcDunwIhAPwFfUbjJKYgdecBlOb/Uff9cIUU1Xd9cLhlHWGhoqDN"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589078917.19_1589078931565_0.9409283429317754"},"_hasShrinkwrap":false},"1.0.0-canary1589094084.20":{"name":"plumier","version":"1.0.0-canary1589094084.20","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589094084.20+f2350b3","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f2350b3128f10c42d27364950df5ec9140f70ad8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589094084.20","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-MtmWE82HjPAfZ7kuvM9LksBo9k7dlHlo3d+cOVaoJPXEERRWMzNbq2VyqyIAaELZFB3RszyXiHEo5TKYOXwADg==","shasum":"8878f489eb1f96bc458cbf63e62752a392636a7d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589094084.20.tgz","fileCount":13,"unpackedSize":25847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJet6bRCRA9TVsSAnZWagAAbrEP/02i3vE05EZScBOhA1eE\nZhiSrqOLXq90Nl0adrI2YnEYeGXFS1l61CT1wHkkXJbYomURL5zfFvCVAXxg\nHU7STFe0AayFtanvmq2YwfL/uWt3dTCEbHjtp8lKaauOO0dje3iFLDRTCKPT\ngu0vwrnPXOvgtTP6R36qF6zXh5gfYocbVwNDFdUyR8BnlR6BjNn59fdTV+lO\nK2mt5m1AyC/lXNEpk9SV03K4Q+9pYlAnnpcTYJSZWc/uF+xfMzQQzDO466o8\nxAQQUMFvlXl8/6YItoYyu7NIMVFyIO0721KK8yuSj3sGZSW6Xu0WPwk/sSBl\n77yeE81OthI0X1ouh1TDSFyPG3JjhMy6IR6BJbO4dq3VSox3hW8fHQ3fYHEv\n/GU7EmJuUasFZNXdzK9Ze86iBYt4qyMQggDyNLWq3IlGdXu+E6VT5ZFrnro/\npcHKAgyVr2Pob3DlM2gatNovzStEESI12fkbyipvxbcynR/SuDaKZazgYX0+\nJFI75o82DYyNylD29rP7cXI7VEgo3baBSNV8yHBdjbnZPrWVRDDq5iraFdIQ\nu4ZL1BFEvO2TN16hwCNF18BHqj+jC4+BsGDhHTeglpwYxmtW0+30kqFVnv3t\ncsAQ1ApKqlMn6HWPDoHQdAwlODze8lWfuXFon8xXtyD6dhKnOpV3UZ0dyWDS\nPey1\r\n=zwDh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDKGlo/KA3XxK8/eGSC1Tx6loCssLfrScoCyRBs4vNKUAiEAgigsGuNkLnqGsaMwWv4Xvv4x03ARxYlcFjMqFYDWh7k="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589094084.20_1589094097438_0.6310733385794227"},"_hasShrinkwrap":false},"1.0.0-canary1589146005.21":{"name":"plumier","version":"1.0.0-canary1589146005.21","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589146005.21+fdf78c5","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fdf78c56c37954e1c9cd01814bed588026aa994b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589146005.21","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-iqLnkopCTRobV3nhgAyGae8ngLR23pnETey0RyEn6ncJIrLlGIZ9xTfytDMTOVuJHDjtbCVUTpddr90YdDGlJQ==","shasum":"842607c492d2b72ee7686b79c0b293005a9ed04f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589146005.21.tgz","fileCount":13,"unpackedSize":25877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeuHGjCRA9TVsSAnZWagAAmj0P/iZgEYUkq0L6tzbwF3Z7\nYsfnAtOVQsK3tg5GhOHeOrWAjHXS8Qq4r3ud/fup6E4IkupRNxVRKiOSPPWW\n4kcIlHhBnohbc+k149gNGjgdv1z++pJfwL2BF2Q6FdS0BrXbgj3ouDMGMlbY\nTcIxovonH7tbKmMOpgomlqCpFBzBKYVc7JqmVSmyS4JlPEvZh/s4KLwFmhJ5\naLjSCM1ylYEzAMi4rOK1/jtzndLu+THQ/I/KXN9an/Lpb6rxpLPaFfSz0O6r\nVjbMnDU3FYEHWguXa43eoSAdtTUbohkNGYiud5H6hURJBQzjdET+IS6vm1rX\nrC8z2P+2ienhImlEUOtYavRx4qNUsH/UlnRBFexTA/uRUtllPrz5e3UIRosm\ngJ1kGBXmti27AScMamR2D057PajtTjpSA5OE0P6IEUgUg8sUtEiupwMJ7r3d\nxU2Bx65UNfUt/4UhByBGGu5yh2phVu+lXMDtvAz9BrGOSYbibq0sHdEkCJ5S\nyQd1c5bycy0/BK//l4HscLuFip1r1NEJh1/uxl7sWnGRh1sB15xPtjBGP4ro\nA5qtBNx7/4Kw5xBaHOwtR0thEfze7gvj8/0v2iy7Xv2efYCJsUc5gQsx4njp\nnmVMX2OoZGTDOPn/NijwhEQuMAwB+cs9zhElRmpc0GfwFqnYBk1cD3P/iN4k\nbK1v\r\n=NQWu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE0rZCzeoZnugZW819wQPC9obOnV84olhA/Xxofvm+5HAiB+xXVYWofpMLF9WOTIRmntSSdzQgKGfhpHOLRncmz6Kg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589146005.21_1589146018886_0.05381776186677345"},"_hasShrinkwrap":false},"1.0.0-canary1589230626.22":{"name":"plumier","version":"1.0.0-canary1589230626.22","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589230626.22+c2584e1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c2584e137c632da68c18cf6158a332655424d54d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589230626.22","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-pybiZ5p6FGtzt421dzFb+MurPhDxz3VyaU291WwATb1o3QQXpQGlMRbnJyiE9GiGCQ6c5Dm3AmELBrG6zqZrIg==","shasum":"c7e371da62dee0724a0764f3c8aa0edb6ec4037d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589230626.22.tgz","fileCount":13,"unpackedSize":25877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeubwtCRA9TVsSAnZWagAA4mYP/iOFExz9ZjvzWi9r18jn\n5N3A4ZRBhAG1gJmxqKctnbP0W9c0Vsz2p6Vq6JV294yNOonVN1WQvJJrZWX+\nIKMHwjDAvi/S78hUsJWc9uaPQSMnKu9Fw5+O9lqSJTnwoUkm7GnRqQatNq5p\nUUbqLntkQ2sB1XEQVrzipNcT2SWajWYdozhC0ViULgOJUVCdbsdrND5Osfbi\nPzYhN7eUCjWa+gRkop4B59S0BNZti3YIqRvvr5r82SVRN7IhgYFxqZhtz9xB\nWOjc9bypwE+vPXdapxSwbJAVXrChFDJUPYQWB+oqM9MF/0Zc5WOIaOyGZEFB\n5cd22hSHmFxLEhdAgCbHnZv9gZabk0PqMvCOSfc4NeUetZYg7xhncnae1Yi+\nV1RdYy7VTCN9xXJg8eivBdEQYlRH9FiDvXUGCCICEi/UJd6/STSodWpqHUWK\nFdcmbtW+Qfin6d4xrENUlNrey12L34lPYHSU0r5BXS+hoSUQyKUYgZJxSs27\nzuNA6Dyi+SIZxgNXW0q9mq20bMXxICisqVDWyLdOX7UxiJQGnSGHu3olQ4y4\nw592mAkWv/6tPiu7KyMyL5kjt6/N5MBvmZKIt2UwcUK6OLtCtKLqPsBz0/PX\n21rPSnx+/sf4/HP5MtNGQ3DKtpvp3zbOqqi8tNswfYJYEF+ffdChzKef0CTI\n5hna\r\n=q07h\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDBODBU5YVe1Du44ejTxeslrIgmaFAS3jdiSPUSBOrxvAiEAn5s97QExRScnQpf2243i15380GdUeeLmQQbXH8PMg94="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589230626.22_1589230637106_0.01693292098584598"},"_hasShrinkwrap":false},"1.0.0-canary1589260536.23":{"name":"plumier","version":"1.0.0-canary1589260536.23","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589260536.23+a9340b9","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a9340b9dbd5aef005cfeeb61cb1e81e2b84f290d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589260536.23","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-+3pD/U6msyJVYFM/ydx3TDs3ukhwoq2CbMo/YfRgMXyQylPTltU+9aAoMmuI2djZdCJ9t4fO7PkcWZsSsZHFoQ==","shasum":"92a2c5463e1445343343861a48559257092226c0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589260536.23.tgz","fileCount":13,"unpackedSize":25877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeujEFCRA9TVsSAnZWagAAowUP/0AgxFWxDcJhnGp/EWV4\nENiqFCxFNTbks970I3WCB4KQxJr2F7XnRRUdQlhP3eCr3/FvyQP5gYKreHIv\nWQMT00PLUp8SZaEg380pTl7ZePBM/wb9IJ4w7cnNsC6wbxeQTTLZqqmfUndz\nMm1on4wZO/tiJ9d9QrBhL0sYoDXWn2o3hOmnPWd/1hp8WlNj1kfkQxz5HZlK\nRE6+/wl6r17h2PeDV3xe+2Eg6BtzKEPM9onLX96ITCDY5pICV4hSv2JMsUFX\nqcjJj0vQ5/dll2WV+4Sgo8thZrMq6yndmG9Hcapflncy257Y+T26huoZekiy\nOP7g+UKOiXe+76O3WCDs+RyEV1RAqiVwXhbV8YW+OwSWOhAc8OEmvdwDfd1Z\n0TRuJlFVJGKiXFTnhMyVrjrrHQWjlju0jJvZkpeqfWZMuuKjEGDppZct0eHg\nJQgQY/wybx3lkGRNOPEXsL/5m17TLH8uFJdpq2PbPPdYmbvlMTO+IJeyApLv\n575/kzI26VPvC6LwRpDKk4KZ9LBqjAnQrKujPaQK1Um18rU/1gTy2mRtmsUI\noLKBsl/UmKkGOgGqkMXVNH67I3adu2AzeTARAjcNMPy8V9Yq/AH6N9W4LgkS\nZAfissS4p1VjalX7IiwCBd0X6nRnKQQ6/zT3pUOKRsy5swVBXboLVjKr2q5N\nZwTl\r\n=2a6e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC3hI4JPY0PNtoE4DV5NRKsHtQdbmGLRSiW8AGH226F0AiBx5fbeUi6c65w85NmBa1R8dxDcEqc8HSJBjs7Jic/F4A=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589260536.23_1589260549196_0.2925360280343665"},"_hasShrinkwrap":false},"1.0.0-canary1589282942.24":{"name":"plumier","version":"1.0.0-canary1589282942.24","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589282942.24+e5b4e81","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e5b4e81ad1c23b94c7b8a2973d4bbc317fb1c1bf","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589282942.24","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-Y8bjETZKeXzLDsWiA6A4M5qK5PFvN9X6ihnoetnOaixdU9FcqFF9EAKu/Uo8nZ/lQqr4WtoNcyMAx8splvzxsA==","shasum":"ec086b734cd46eb2632d145f7a5e4cb36601f420","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589282942.24.tgz","fileCount":13,"unpackedSize":25877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeuoiNCRA9TVsSAnZWagAANBAP/Rz+Wzd6fA+qUIuXldG0\nHjb76MQTU6n0HgZwRKPa9ZFcbmqiHNmcA+2hcPf7/wk8h8gTO6FuQRGpwSWx\n9WLOjQqYP0uuyLSBu3mYmfeOrv6gVzcxhRKvPMO+qBlowaYq7wJpm4plaqPD\nt+e3XzjHuIlxeE6swGxnzu8cHqTNt0yMA/zvs7LupwBokUQQDk8Nb7VaAaBW\na88LkinXD6vb3l/WAsfDHXRPyS7wZ51hUmUMSLfjjQdFyVmItTbrRh4zctVe\nSrGXPBKBKv6VkEUwyu7QK6e62MKal+oG+2xEbxKfDjLZ/pi1aFVkND+oNncf\nEsyjS8D3WTYP2unpZUQ3EOQ2BG1QbASIpKZRJ2FDEl6cH4pwAcndQxPJLIET\n3/QLFKo3jhEVhuHFHTmv485hrwpbOt7bfSk4DZy3v/37cGq8cCMVz30zzXui\neuLs87aP2sWm6+Y3n4DuvbCj7Wn+IGwq0maDPxXID+QT+k+n3N/s+7CnR/rN\nAuzphKo1h91gloCuZru20w8aCgVLr160RxMKdhaeoKZi7lE31OSRxif3wgk3\n0u1jeFhtbJxtr2S3WxCW+A7M/DGOgR9D45B5qzPeehe3iyEkQRMoLfPmpYjH\neD7UkP5joxhleHFyUgJ1RfSzqAdyf9HPFl0muGtRkVBVJyfTw03Orn+qqi5t\nUPFG\r\n=OmxA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAmvRN52Tqh3rf2Ls9e04LrFqR6kcnH7pnrz4mlm/D1CAiEAsY6mCxWMGxGMJz0HNjlUcohjUlBO5XPz1ES95TiP4Hw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589282942.24_1589282956755_0.8270887957722357"},"_hasShrinkwrap":false},"1.0.0-canary1589286463.25":{"name":"plumier","version":"1.0.0-canary1589286463.25","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589286463.25+a9b8085","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a9b8085c27384ec520988d0d1cb6fea47821da5b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589286463.25","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-AVlcVBT4wnAFseDa0WgSDPgHgW/j6C6rlc2RVNmDoFtjZMFBhGMrt5oDP852iX0DUFmgvird3Hrg3GsomtAqQw==","shasum":"27da41766b2566b33e8032ddde2c627b23d0ed7b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589286463.25.tgz","fileCount":13,"unpackedSize":25877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeupZKCRA9TVsSAnZWagAAIc8P/0Noa0VHqTos7MfBV+Bk\nUhf9vLUIOZRneOo3ycbpgPpxpmDme5kTUwxXpFCw/415nqQ3RuyyZWNPjGdU\nqnNtIKqIOtcT0Fc8F1tDXHSFPl2hLPeXJEsXB1FSMBBqPN5lwLnurR6Eg2zF\n4ZzCjwpVyOByVU0/YUscChNhB92m/4hT8R+5kAoTiQ/vw53BATfncBtAqaEd\nDWRiav3ljP2ECJdteu516s1U4y/ndKSZtpqpuquw8ArMXoPwMUwM+VBJVhdG\n+6WvNwv1dRYVdbu095m2bOv2xHNXRo4uTsMAOC3l6smEzg6zy9Ct9C2ooXWk\njXsGxaa4P1ZQWryMhxvdzD6Y63mJ0gA/BLSx/yBHw5fI2zzL4JhGSeixqL8Y\nUGGsDukiYsy+mBryYdpvcp20B0eT7ybE1wC/P4vIaTS02thWBf589KoiYq3m\nOClnhhbPXwazsr3ICJa379lYU4kt2nm/zH1jrmW8I1YcM/J28Fogxhfx0Nrt\nQ+3aRb/hRkFuTb8OcJuL/aB3BrvXFUGCwlU3FTYu50WUaufGL3EFdCcZbAin\n2gqdiRmTMDIsk7FvmRhMZ0iJuY+HScKHn7M4l+wqCXjs6f8tJ0zGaFEX+LZx\nPSw891lTTzseJKf8+FZSj1jDnsBfMM8OtKPDj8zVwlnNlwrjW/hO2S7mwuwh\nCFTG\r\n=5REL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDDclVknpBt8Fg0NEH7e3ym205hmWe9H3ikrjWljDXPhgIgLMtjACDZpk0eoJmDCOj/TRSfG2cd5rUkMD/7SjxDSmk="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589286463.25_1589286473739_0.7661300936027022"},"_hasShrinkwrap":false},"1.0.0-canary1589432571.26":{"name":"plumier","version":"1.0.0-canary1589432571.26","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589432571.26+44f5dde","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"44f5dded7b0dc13a0af03511a22521ec24a9b95c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589432571.26","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-AEiZul8D7eRSlKMGzNkTwZj1XBmYObf0VnNH036FX8nugRpEvaAn7zdH2KYpT6T4YM7E/zJpc9jCan+WpvXKWw==","shasum":"aed56b64110a8f4a5f6b192ecb9d25e86d55f5a1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589432571.26.tgz","fileCount":13,"unpackedSize":27668,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJevNEJCRA9TVsSAnZWagAADwQP/34zbVW85kMjUjrfNrQQ\nQCqlq1426p6wgtPEFR7tYNsqxG26ECGO/QMBsb391Yb2X0vc4pzmruFCvPsS\nE4kJ5Y7SWfE2QKUgqPKUMSoJoBm7229B5WrSPigSjwxe/FL6i6jMojIIU0bm\nL2XyffV0j8GaSYs2SkPYzroRQ2ugJh8fldxZPuCeG4/lspku032QzzFVge37\ncMvY3OEzbxvbzRXCmAxiUWlU1BZXNh+deJ7QJxANaxcgNxlK65EnAvN00baT\n5qPgXMgcWNgNZzdaoneO3y3N9zE5AiZXjwbvlGYZD0KOOsENWMSYJnmT24uL\nEmqgTqxAKbaHCLvUrlOaS+4XrGhKWj+JMDKcZ9WzsYcitd6PXiqzfeWKYqeJ\nYjCRPGBC/XNXCWOOHT9h5fifuMkCD+6EFe2oaBtyoqm8xYqvUIooeNYdqXSY\nRmG/KU6wHs6YKFbZ25j8AJuMtvVRfPq/Uf7viyMfru26NJ3DSJlN+Kh4/PIx\nC4B+ioCCg4a1pHiYcuseNDD5t2fa0PXsTiZFHeLLEV4zLfzEeq1WPmvYCnA6\ng/7VekB90d56e3A+w2UfBxAL8PFMWalKNmEgFaqlrqAz1xKHY3SFqloz9FRG\nceiqDm8vCh8cRvbJ9ZjTTH2fAbJpe1L0YnCAZ/deOxIZEV2IFUc4qhgyrdXm\nHh5b\r\n=WDSZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC39pVe3jcxYtmGED/QfLh8x/7a5OPXqXm2B9I6ofU2ogIhANh3X2GtuKGzKQV3MOs+rasNXWkd8X9GE7VFzgTJAXyA"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589432571.26_1589432584615_0.8549726411960954"},"_hasShrinkwrap":false},"1.0.0-canary1589580811.27":{"name":"plumier","version":"1.0.0-canary1589580811.27","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589580811.27+6322659","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6322659386ac2cf7fbe1b28a3125f4404c8d0d63","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589580811.27","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-Mr5e2luKaLfO2jvEksyKnmY5QeNzfJQvKWeZ4f1QIZbUzLvveQSnYc0+xBQZuyYIdB4KN6d4iXtGurwabNcC3w==","shasum":"8ec960c4cd9b6af8f795f6cff0ea4038747394b1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589580811.27.tgz","fileCount":13,"unpackedSize":28314,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJevxQXCRA9TVsSAnZWagAAUFsQAJ+VrAT4+oT1t61iITr8\nhH/Osw/5dJNldAOxLtOMbF9SGzoQLL/vruhza10903bBEDafyTkaJEj/VV7H\noPWjH6OXC2PsKIv/zXR902ZsapgOGFwxQEFKZ2Iitp6NU3lOFqlmSTDdwZHx\nE0N8yVzxI9lyuGQiHmyzrLRw8pwQMXehOPnipglJ1caH1adGDl8HMWf/hYwR\n345+MH4DMGKVebsVp4F75MK0FcHGJFQ+2bnXvQVxU8Ko2qp5bE6G/D6Uve6m\n8DboCLqwlaX8+rnw+YpxWq7o5IYtfIMEPZ3ps1qdT5xw9sg35sOajFu5ldNa\nTE/vQSCY1t+cU51S1aFdKnGEL2byNwM1eMCj+drpclqFWLG2Xmy5pW0zhZ21\nkF5hyBQwIMdJyTCeE1zdv/wDGZThKFoqJDt2zutiaN+lcYGgQBX9Yse/rRJY\ndXgZpEcaE4tQROxL4uOagfMZu0DKGCV4BNpWhDbomMapFdKUMy08Tr3lmVWu\nk+15ozBZMpyTMZ6uLRimpC4Om2mkCfuM/tkATARRqLyCtUrEEhEKaZvKMgaA\nQFZaxgcFsWgPpzGlpA8A+4ZLTj0BbnVZaWlg0VeodLom/HzohhtJAeFmJAmR\nXYOFqYjofLJbssMm2YBGVN2/5wtopA2VN79/YFABYRcuqgeQ25fIx1TsWqMB\n8IlR\r\n=jFtL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDGpXE4Y6pw6DpkOLK7IImprzCOiXlJc6tFHKAzeNStswIgeelZZFHarg3DCnKriMSQMlistCNGGUhUS1hjl/gfphk="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589580811.27_1589580822766_0.5716089138012894"},"_hasShrinkwrap":false},"1.0.0-canary1589604821.28":{"name":"plumier","version":"1.0.0-canary1589604821.28","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589604821.28+665ae18","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"665ae18e10f60f5aa78a0770b4816c7acbe81597","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589604821.28","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.20.2/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-bN7Oqxp5yVRXPeoEkL0eBIuYUnM0SqN0T4G8no8A9hZXE6fUASF8nFpmQlqXEcP1B+bdTOBWmP8Bm/uZaJJGEQ==","shasum":"74a715d65ede8a678bc5d35695083e6909c7ef66","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589604821.28.tgz","fileCount":13,"unpackedSize":29580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJev3HnCRA9TVsSAnZWagAAzKoP/1PFNowZ7aspMn6BIUci\nvOqKrKZjwK57e44dzkcv4NRFZyI490ZLfW7laB76oWA4hCHyO194i4qGG0Xu\nKBZ76Mo6H+W/zD+Zg6Qvozm+N42mxut+zfpmdzNQKwac1g2YZ8I5AFWMK2dV\nGV6uxZbci+nIWc9V8RHPImYpGzoB1DJqAW3jymWbLkr7vT/4mu6zwKYAutpo\nXYoW+iQMHVuLGRP4PLWAMI5jABvhfbEjzBe2eGTW+FkigfNl8aqxlMJ994kc\nSe+m36m+D/f+jTWeTPV00mGhDINnTtQ/ZWcYm4O1goKv7mheKYdlgzqpr5Jh\nMHleEgb8RD+tqGuMvgMeyBJ99yH3+KhlqjoMgaB+GoufbFJXwhmjene5QPOC\nOJpKAP0CTx1a9afhnYPTKH6I+W76u4qhP604tqGtlb9pF3yJb/GfjZd9VUQf\ndsWz5CwiAzySuNqrNW7h68h8eNjKeAtzyCAwTaf7HpybpchUwPmZpQn3XrTL\nGinMuJf6vzWDG0LwZ7rwWbtuYFcuskrEcZ9z/fbU4Q1mHBuUjNrFsi3578/l\nFUd6shkHcOpLLf6Wkz/kHdNNP7OsM8TOvB4Esjqp0CQlZ09U+T7IfeYMXtzE\nmtgJptdkDUwQevCYY4FD1iUfpFRy0pBVl0R++R4y8kAnZzhkgclxNDqovaMq\n7bG4\r\n=aQeC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC33MKyPMVqPLGLsf8yVkxqH3OlB3zGfwBaEPkfpD98XAIgLMi0ofgHlC0UdLj14oPKBN6gXEQUMKEveqyLo5GtljU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589604821.28_1589604838851_0.5893965208857994"},"_hasShrinkwrap":false},"1.0.0-canary1589614058.29":{"name":"plumier","version":"1.0.0-canary1589614058.29","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589614058.29+3e2284b","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3e2284bde04359ea920aff7eadf5c20f6b1f84c1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589614058.29","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-aogFaW/PNnYXj/bjj6CjO2PzlwZ9B92ZtyTuKjGltvcoc1qKiXeHu8DYZNwFiCI0thDoQGizexJxcWZgpIoITA==","shasum":"30dbb2affb5e0fe616988ae7728777634149891f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589614058.29.tgz","fileCount":13,"unpackedSize":29580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJev5X1CRA9TVsSAnZWagAAjWcP/0bfstjsO4UZxhTO7JUn\nRRBJi8hZ9f5gG47pcbmb/roaGu5vuQA5vnWGuaMT2FIJIjLdsLBvR4QIX7+t\nfnGXKz5E3IfojPbx8fGJc5GqTsD4f14HEIDXG/fQIjtuTgGbBw/Kv3RQyYnF\nRSU+xpmgiJ8C3/hM0kpM78ZVgkm727lsLgPuoaMMc4M0Kvc1sEhm3RAZae4B\nYAxwQVJS4aoiJoNm46TQAwF8MVs8S8fIfvbe1tLuyCeQzOOYlHsAMPyC2jg4\nTIxZ0QLasdCK0RulDKuC2h1VTjw/9gqyoFnwp2umrA5Z9RIvi0XLPVw2A0bc\nuRDPnfFllfzaN+S8eOUz54maJ5Z0qnlpveql9HpaUfDlWSYCvQTy6CPzLR0O\nBt96Xbv+IRNSMU7otieBBCHwqLadEUh0IKOOFOkGU2XVVHTthtQtytt1PfJP\nCTrTISUWuQ8nAFs8AcVJ52Y7PhvVgmaNYT35en7kKDY8lmwk0UHXQtV8lMh/\niP3/3uF/HSc9xmot4SroS6T+V6+sybikigjZ8SqZyiXb84ysyMA526hrvp+f\nrsifhU2mXbnaS4hWURhXPsgV3pEVlf7xZoWjy2I4AueiYj1k2SqLI3gDrLPO\nlI4z+1fShPm2itvfawqJ5fuKv375958CyG7wPRBF845ynf0fmvhGMuYrcscO\ntM3a\r\n=OwX/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDyJmlaw9e/GglRzY2rBZfnZK5S3QJFxhfGODTukbmGpgIhAO/zcO53EEGqIt9SJCXvzZ17I/E0aTZrpRnD4whgFx9N"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589614058.29_1589614068941_0.3272422251427298"},"_hasShrinkwrap":false},"1.0.0-canary1589675109.30":{"name":"plumier","version":"1.0.0-canary1589675109.30","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589675109.30+36f3ba2","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"36f3ba24ec7cdc6ace7ab977b9f4d20b665fb887","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589675109.30","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-f73GUUwWmNnEjojGqST9dSsy1IzgquqFRO7m+Wf03mV7OM0v1IGw2YIkHmiOdNVnuXlStrobTxLum9So6XCcLA==","shasum":"1cdeb61b82fde4d0868a4af36e766846ca8c699b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589675109.30.tgz","fileCount":13,"unpackedSize":29580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJewIRwCRA9TVsSAnZWagAAqQYP/2uBZnXqE8BxSBJtSwvN\nEFavm1Xxy+Qj/54+jRQlrshMefSrp6SUFKp5AEW+b5We4g9ZwRGNAPdPTuvk\n5llEHm5tZKy2oL547RD8xsgbMCj+5TCeB2gwd3vTG7kIgvV8tyr54oU5bgon\nkh95OOk0lR93XKF7V4tBjAb8Jz/Ke6RW67CEEjroIoYREDWZpRdKywviselZ\nlqsO916P7NVZALd/S61+KikSSOqtJkxljpjCPC/YTZ87JZ1ixHlln/TYHaXx\nvgXs9gam/TbMx8Cj7PGmi5qGdcUjLxS8Yu1DVAx632hLtlKzRtngSPu938FA\nkSY0H5uaSxn9mO6OgulAS06ny7fd3MdcRGyNTbH+Q/q7nHAJVhIbKFvjRc+G\n9F9ddfIKiqYNO0hRKuZGIMTkkMeEP1MwFhqIAlgYUN1jKzoLNKOBKehsZGTD\njp0vgJydCjjweMeE/fCn20Ilym947h0dHA0RDQCZbHmemf/cQjyOjlrk61Wn\nqibVycqgbXL7yp5GM1eQFGsKa1WDziHH2nVuD5MwKyFuAAe1GxcgnTrwJg6K\n+6UkX62ORvt6TaAX3vKbdLoIsr7rxMVtT4tHGo8wQtsMB2bNX9ed/IFQZ5s5\nJjUuwrO5ejL4QhmcfQIDo5YWBOLM9ia1G3jkBP4r/X365cxr5i+cYZ3W6Eb5\n9oMm\r\n=tD3j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD09rnoTy1TBJPgvBR5JlBqaTe0uhgXVsrxKC4SlyGgtgIhAM9puT4uFk1yxPpAKANTy7Rs/PGCDVpngLtRTRXuLoZ5"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589675109.30_1589675120301_0.9090215775435364"},"_hasShrinkwrap":false},"1.0.0-canary1589682003.31":{"name":"plumier","version":"1.0.0-canary1589682003.31","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589682003.31+df3623b","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"df3623b4e6558cc01406d8b5a059175d1673e80d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589682003.31","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-nlhKGz1aVY75mPWs4nqEfOQuyl6zKhy2mzUYJrk8JJRGpbh43lWRqcFcrvdZxtSmdI6vwGpg5K+I/+8lIV0qUQ==","shasum":"80b3ba3218898b97a1b827865987208ed75c206c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589682003.31.tgz","fileCount":13,"unpackedSize":29580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJewJ9fCRA9TVsSAnZWagAA6JcP/335FcWC01va1jAZKnKF\nzVrZrvPiqOzBK2F8RvfQ7ZIoox7XwFfaW3YNwe5BRQmuWYThzbcvmoK8eVke\nl6eNGkcjhfj0cqDYTTHhn5mk9W3ZtqvodqRWFwd7OsPE8H18gljZw1AqljeY\ndZ1zQ30ntZxkh+piW4VdLjF5oiNTv0PbDzogMkiuGu1fCKvEWiHkwZrXruT2\npYxOdTK9IU91W1qni6f3kwF1IUJYInulNVuVWWnDpEtEKiyDnzub53PhT1Od\nkznzpPobCM9hM9Cm0ZSF81DzRfMyFunhrex/6SM1mqPhnIPzjF0K22l8A2pp\n7WznCD8R2W7fl20UMDBrjSm0nxArubZ7sd87Kx/kYavEqFNXnNAgojxakidG\njkcACyqX/70r2bzI/f/JHBLfIiGqUiMQeTJc60PbGhaOhgq4zSf1d8UgYtIU\nbAIKRvYUOseKFKV4GdxEVUjWfmrIbPWLrHlwAXGDGuTscRCi/yHR5UmrDbcG\nurqaid0QiiazFkHX+7rKjSc8ADm1dez3mRGTz/k0m6L8dfsX0XLVppI1JU6B\nsVwRNOaT5cFmVEtf1urNbvRI7GaNcMlpPHv+Rnp3Iz93h7yLQkoc0DRVdqCQ\n8eNBM67d9tcwNAa/qZb3rpK6+ByX+sGrkMy88g++bNkidkrVPF7bOBbKh9GH\nGY7G\r\n=I/md\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICHMHP8AToTqrOrJwNg6QwC7a+GnUEK+MrgufE94jixFAiB7SpJ+om+nsFNo1Up7kBPPLlSdaZTE3+1ltD8OX7D7rw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589682003.31_1589682015362_0.580296779174114"},"_hasShrinkwrap":false},"1.0.0-canary1589712356.32":{"name":"plumier","version":"1.0.0-canary1589712356.32","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589712356.32+09e3cf4","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"09e3cf46e16bc61d50c3deacc4f712a9ca440389","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589712356.32","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-6+0JJSFp7nuyL+ba8Xx0lwuYI5FLCwZHKxihZwSA0wO5L/AX99LE1pmrxU3CaFGczMEG9E+zbhYelK4wS8I7Tg==","shasum":"6a848962fd0f5573c1f5078e4a643fb8882aedf8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589712356.32.tgz","fileCount":13,"unpackedSize":29980,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJewRXvCRA9TVsSAnZWagAAT+wP/i1qB9JPSOfG55kDfKlu\nSF6O/hTIwl1f8pDYwcP3TugXhnWMPtNw34OEvvGIWGJRa/xuLLfJDMDOwUdA\nCDf29I2LjwHa4Xpo9ZSzsmSgryILbr+mG7/eZu/UvLz5mix7vf+35rBYqL41\nrkEjGWh85LbcbPxJsO2/PPjmDtwOArxlTh7DFNftFrfbcBt3Yh1rQqHTstv5\n2W1cCYXQ2ySS8hY1TYRZxQHcwlM4r/hHPzETjk4zYcwQ4p4rg7FUitv/8A6q\na+BIJjMnqnz6FAXaEMsxbFTwi5z2F2zO3JkvA9LYiIDeRfBHAXhN/KBVEtqr\n9gxqS182x+oSVx/bajijV/fhhhMuBPmWgdtoOrHTFpuVPhojQ17lSczxz+qD\nBbIezPZj2K7t+Gzl1Ekg8ICZHsXTMgCVpM6daLa74M1gbvYGnHkXNuxvpFKo\nre+RW326mR5MySXhQ6vX9gSynxfbYolvStj7cDzzG3EOprHXngBGhFrwjmaO\n0NHvY2+PjphTCDtG0vsV6zECefLaivDhrjNA7nufSsfZRTV22GCeuY/O4/NU\nrTj4e1ecIN48DreUM+PM8hIWR64xvfcS6YvSHfTtnWDBnbUik2ssK8iIAPHj\n1Yo4WgpHQUHyUJazOi4lTxW5IzXVlM6jRkFOhhg+oohwCRn/PWLwgg1mVo2h\nZSqP\r\n=GnrU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCa+jqV0m32Xcwd2pbaPqrDIBfIzWPS+lIMkduVAQUxDgIhAOjc/hBCDypRC4k7uN/QMkW1WJnH9rc/lUTgAepOAARB"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589712356.32_1589712367444_0.8682557378925955"},"_hasShrinkwrap":false},"1.0.0-canary1589754316.33":{"name":"plumier","version":"1.0.0-canary1589754316.33","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589754316.33+ae13c64","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ae13c64a03e415ee97f888e696639dd74538e6e7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589754316.33","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-mYFAd2UudqN+GGXsb0+gmH89yDOxiqBvQ9ybBNnzW88qcPCK+HefPz1wuqDHn9CaA+mdeq4NThlR9/YXBHwHaQ==","shasum":"03aeb09a1412f1bdfb8c311931bad9228b9c9994","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589754316.33.tgz","fileCount":13,"unpackedSize":29980,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJewbnYCRA9TVsSAnZWagAAXqcP/jSxhhEw+/Tvvy4NexJ3\nyMcWUytVt/eBi+SK+JdrQDnCMKlIenCujHMEaIYijPFCUUvwMqb+4G4aeMNt\nuX5auQFOVrt+A+oGWNXYJbctX142bMOxZAYx6nmqxKBGr06D92B6rJ1JGoJs\nuea+nVl/MUKACp6RaL6HvGfpgVPnxHt1MQfOg66xH0EyyoNc5VHWp5Bj5bKN\neRXGRypFuxbFM1reVvuE9MseKUO5Pw29938ocXIt9/nXcst4Pwe+dbMJ8L0p\nzhogUwnlKSV+wwjAOu+A3VLyOKeTU8EOgJJ3xLg6QR84jlhOJ4C8+ja71pl6\n29v10l9r/E0QcqtEf5q1s7uxlZC/S+rwzKXeGG6lVUDP/UBRDLr8C4bghEWk\nQfWDK4M3sKAfyT919QqhTbvaeKhFNcDGUdqD99uQvgHIhTu/oMlp0AFgHKL8\n+jXYpCVKYQYW4+GTCFZIHL9LUlw2tfh0AWqwSetJjSmhkYS3mVeyeqSxE11N\nid0BspEVVyE/CLwP3UQ3f9yKBPuj50psUdj+gMxYzoPlGhbFVhQDpIufCnz9\nP0S6rkYfTeLRteD4OojKgxpJTChXeprmB2w0qUFmrjBLIACDxioTq8yypQaN\nWwBtbkUBuYO1NfkYoDnmdwL8rAa1K7LBofyXESt/eqodTodumroJvGp8S6uN\ncvfp\r\n=Q3Vl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUAi+fyMMQPnbaT2iv5d047LKANI3bKR1ny02lcm9W9wIgDnr2Ndnx6usX9DheNhynRtwfDXAu/+pUZhJQ321ae8g="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589754316.33_1589754327802_0.8988113459728455"},"_hasShrinkwrap":false},"1.0.0-canary1589788028.34":{"name":"plumier","version":"1.0.0-canary1589788028.34","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589788028.34+d646d5e","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d646d5eef49ed5ac13983dfc72cea19a140fbca5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589788028.34","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-SF//dfQ7UoNhadu2aOydL+KN1DhmTBqDYPoBr+cbFFEfAR0YRc+SB7SNYU3NMYMpcmvGC0IX7pmRpWbzbpRJZg==","shasum":"08569ebc48509180baca7a65c1b9f0827fc2d34b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589788028.34.tgz","fileCount":13,"unpackedSize":29980,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJewj2LCRA9TVsSAnZWagAAuXEQAIOA2BX5/GXwF2uz34zt\n5Hq2OBVpHAUQ5m8RcvQbimbDMWGd2q4ZSNO/gYJzOvr2ozoFxRqDFQON+wIX\nxExBzouKXJb4EOhltdJgfiRG9eejg/VadCjRQmvyS508CSy41rs6smwgKtTw\nZ49GmabYWBTKIVG4YkWnGQW8rAtSlS0Q2rV2Y/1MAYjuwEmU9EeM/iHWpnB+\nb76e1fDkJCpr5WJgV0AaQu2StilbV9uTpNFoYHkq60hwd+A+4ZKjVRvGeC2C\nLVBqBRQyvAXRYFeymSYwow2fik+1YwFCuA05kM3/XfJejqyS/IdpolesFM+3\n8YP4jSwMt9Hy/zpqxFEBiqYkXbar6wPwLnodYn7Yw4uOx9XBoDOkPtlP1sI2\n5/PDlvJ4BIh3ftTvQRqmpEu9cqyOu7+8S1PphsmdOiVFBzsVB3A1BtZKThCM\n+QKEaAde637+iBxOyWp18RmW94cnitA2/GuiIumneJiV+vA0AWzXx/HtM8Ol\nVuwwapf+FcY5VyTaacFJKlwRuEKpPYCetmqSToJsK5Oqxmy0H3tBwwu+ZAZX\n6Il/gu3iO0bmeK9KGFP0cu8Jg7IiTlxfMAciZvHpjX5f7jCBk1JqbdcV2jnd\nYUiw0Sh3O0dplxhrx1UJUfZrrBWUMh8yNlixNxP4PmmgVfYxMLSiku9YjyTW\n5pIX\r\n=iPec\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBjI6DyhtvMG0R3kRNTeJonykZLyJU4dKyY1OD8k+kP9AiAyTcZV0/MEENMQMmJDKFQDUO2QWVOH0Zu/8i4ALE4X+A=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589788028.34_1589788043283_0.5886523361124756"},"_hasShrinkwrap":false},"1.0.0-canary1589863748.35":{"name":"plumier","version":"1.0.0-canary1589863748.35","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589863748.35+6205161","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6205161eb07318840a8f50a0c53ee76d0ff65017","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589863748.35","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-u7vcy6XOy7jGBXbEXU9ZnRymuSKvAB8lt4CVrsJgSEvlpBGq+D54FiXZsiWzoIY/2GNpSoTcXxsynn/0tI9lwg==","shasum":"4466e1420b0d8dfa02930dba9646c3a3fcd170ec","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589863748.35.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJew2VRCRA9TVsSAnZWagAAygEQAKQET5d5rQJ8CbvTcIx3\nZbaP4/AU2b6qgZTr1TKJtT52RNRIqQdzAZMkB4ylkJcFKaUM5T18fq5HC/Qm\nQIuSou4e90kh9b7OJnIadrP+qo3dgf9C2oESNynQ0riJ3qoSldwGnK8sSiKx\njRvrNK1hXz5ghNXWvdAXCjjewn6diKRL+pUbZwCvPxsDhkmBV8J7O43Bt2EA\n0eqqYWqYp9DPG6NZW1/eYtoa6sw5jicLFCp4IfhR/fv43SCv5PC8E4DFndba\n8ra1kczEqRsd3AkwmR+1QfpPzYhLmEW7/DY5fncTKgKRGCYWfIGaRM03t2KH\nKo61CdWDqBm1ogTv8/bhCQJakAzpyH9Z6RnNoBHmWwo6WB2pJRi6/f2G6MQ5\nVgxcVqwlJ55sqxv/t9zhfXvvPXBaSjaO5sq/kBo+iBmasyhPBHqGhwwWIUUe\nKJUQkviKv1XPP0M0Ig4uXf1Q9nA/+SIjNRrGoUJpVLfLToMAfyKSyMZwpOvZ\n3uLdeeMxayVBBDkYYrXSNsfDDTuulvq7DwPRYsIRhYv6+bZ/kDD/l++vMXYB\nQBN0GnY5Kpgfzg3JKFmHyLYFos508+LwxeEnoo0xmWn0pmOEoeTopK0Xq3K5\nNjnH3IuEFPHgxYlsw6QZiJXyoiIlxwXqmIidVy/jn2TuMNWrONklrTkJHCqe\nC2F/\r\n=i/mh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDqEQgzn4X8EAp6GOmXt1Co/zua3jUWfEegAiHoOPnjeQIgTYAYjrKdrVb/Gff6BKXS/ihfnAYDP71kx0U1FEczjiw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589863748.35_1589863761130_0.8461832402398992"},"_hasShrinkwrap":false},"1.0.0-canary1589938683.36":{"name":"plumier","version":"1.0.0-canary1589938683.36","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.0.0","@plumier/core":"1.0.0-canary1589938683.36+8865ea9","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.11.0","koa-body":"^4.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8865ea9ebfdcdc6af4c8bb5f84c011c747427c03","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589938683.36","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-Xg08uepMlNdEkcjoZA4Er5y7ucjtZsXAS5eEbt3v5eOSLaijNFvcMm5FCiihTGGa3BCbf59p58NcYO211H+0uA==","shasum":"6fffcbf858bee5e0042a552d14706e4c2ab5b219","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589938683.36.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJexIoJCRA9TVsSAnZWagAAoyAP/RgXceKvox8TAT6j7rw2\nE6VcoJ2/gM8LqDkaQWM9FKUDiyoKf1MtjKXvTBvQxr4iDb712BmZEQYxzo9A\nncnJ5ibq+Vo9FBuc7r7R19/21mf/NXrDfyudHeumd8ayg1jSdysPraQ/yN7C\na17q0D3SMouHhTiz/R29unka/adcDqKmlwC0Ta49EJjevHckQGDo7318BmRM\nr4KvaiRkd7ITvpW3psts7CGis5VOwVtEAGBTBs4h3sSh2TPnOG4YsjIoiNWS\nWNzAqjvfvOcIE2+wIQPtdAnm7h/0r52etipJREl9Fc/1FajsnRibezCLM04d\nyooNAIwoVPly+FGTlY2foQSE7pvDi2Ih8TsMJDrI7yOK5OXz6kclB3a5CDV4\noEXuFEgSMQ3QqvF2qx7I+nZGD/pVxVg/tEXmsIJ9G78K/MWgFhbdtx5BC7nN\npJQz/8ErNF0znzNFAvw6N4bPKsc48UW2DMw4ZSEA55U/NFrgcvZYA+aedKZm\n3PVbM/+YsEXadnSoAyhAu0A9VNjn8AGFViMXQ2mK/T245iMK8JwBY2qx4Ch1\nkDalQKV+DVoUtzpJADyfHT7otcxsz9CcD5AIRSs/JrOGDJksd1ZcV68G1mzh\n6dT9grJd9V8rjitkbXWCLdBhQPKMPzjIC2g+iIWGzkDIB/t46gFzv4aWLaTD\n7Jmx\r\n=6pda\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHc7z/LqaokjWbgWYpNeNCBG0jhTE8PmVrqR1amY1aK3AiAsRpoDvBw9cnMZHpzt0E2D/3uLx/DHBacVZXbDV8Pmxw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589938683.36_1589938696954_0.18053247935177286"},"_hasShrinkwrap":false},"1.0.0-canary1589957613.37":{"name":"plumier","version":"1.0.0-canary1589957613.37","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1589957613.37+079b05f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"079b05f7837e5f981e7cce4fb521f9f510772f83","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589957613.37","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-Y96eS87OAtFFE8+8BHfm3TWoaSLXHt8bxW9JGRFOI8k6X36hnN5mOdGya42cl3lRb9q8BlYt3yhS9jRxrPlYhQ==","shasum":"59b234e04d9e3d16cf9b9bb11f3d3802e42b7d35","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589957613.37.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJexNP5CRA9TVsSAnZWagAAGWcP/3DAk2+0VNMAb5UmXdwF\nD2NTjG7bHDYd7r4kS2+wDDEKU5D2jyPa8euqtxt0ohejg5X1lU7HSQjO12Cj\nUScszuLYxqmuWtq0d8kL9Lxz4dINuif1HdpvCRB0Q18ajQpce5jnhYm0N0FG\naVeKKKqjBCwCI11/CQJLC5wQE0qXBnv3AVkTjz1MDjVHnPYw5XmtHNvZTiQZ\nJ7izQqbtH0BddnS0zAPXpAlb/QPEA4wIWN6q9YIM0YF9/5wsxy+TTPoXQfzv\nV8xN+d3U9I2ZZEv09oP3wDwic89ZBEJixjBZmOP3ogjAo4ekWusjYGnkVEQr\n46/BW2TWlmk7gUWg2mFRCk+7Vbj3h9x9evsAgZHLaDRuFP/K2c8AsvCVtwtx\ntLnH5jRMRh1NwPfoILQwTthj5OxBbJahOAD8YqmaaAjMcQPm+8k9uzOQsCSD\nqKRmSI0IvhbeDpmBieIag4jaxSeI6c02N11Lj4/1U7RHN1Lwliwun8ND9erP\n6UuUt92NIM4ZBFT3diGUP/il5+1W1AQjk8ZWpBj3fx9b1qe+uSw6XH5p32Jk\n67Wv9qq0Evdq4vT12EBmeApF7AwXS4z8asQLKw1G4t4qCKK+SFfq0h5Q42WN\nsFpEQKFYzT2oviOEfzIuz/ScWxnR+TgXZSj/rHQMKyX1T1TITikjWeVgVAIR\nqvIj\r\n=8DIx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCkbe9LBBcnMdBg1ai03TtFJuh5t2LgRoHs/yuo2J1akgIgHL/VnZs7ifIa7xOgWDqsw90YCPP0ZRkkyUdYFbHUk7M="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589957613.37_1589957625365_0.1026251525967472"},"_hasShrinkwrap":false},"1.0.0-canary1589959818.38":{"name":"plumier","version":"1.0.0-canary1589959818.38","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1589959818.38+9623e17","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9623e17021bdb6b77c1cd09dbf947c9602793f2d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1589959818.38","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-EG4xe2yTx6+RR45Jak1b/OuzKOlscpk3PYWnvw5ILuTe3iO31BYfvi4v0Nn6hkOjlaX7XAX9qxZkITGRt96L6w==","shasum":"8a1d4313ddcd438bebcb04a4731b476a257bd0ff","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1589959818.38.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJexNyVCRA9TVsSAnZWagAACMkQAKGcFD8sMYeUOkuT70o8\n8A6ofWRnHYGWpEH5Itr0vOMyEUQIuLlzEbLU5hzYnwxUohlihyD+PXhfhpXe\nSZmYh2rzdr40SLJKlf2wXcq1QoYvxExVEitb9issTSsq26TlE4KohJvX9pxY\n8CWp8xbepWvptJzZyFayXzDH+VLFYaujCjJguun5CpYRO8fSZHzZMuRotrJM\nvOZfisTnDEed/A9ZAlu2SqUx5hTDyDefqbb1NG/ODNqO1LawRtUM1f0xhKt3\ndLUKn44N40dAp2TNeys4K8+vJfOFDp+txWq6RCsi8jmqfMF6WKD9/t161wNm\nUW+51YBPVBN6aKVvnKBha4qV1HQXX1fFlqOtc/6wsnvWO0vG9KUeSxcFdxQB\nL/sgiPgojLdiheQhIZWCFXKtAZW/zIx797vuRgeYd1aErg3jmnv/MrnWD+GG\nOblxBWf16F24/RfRpsYSQL/ngecxh0knT4XL45EA1cb1WfTqG7ZBjCU2JErT\n/RolIEWNUe5FXGXBJkPBkzPiuWQ+mvMoPe5jMP+nzLccrsyfArBeQp0oYICb\nIQ9IIfpCq5qHIzeM51nZBdSVQLaiEMGKTGem5QOwcYam8ylriKGyvI72cUr/\nxBj6Cgm9rH5qjqV0In3xbqhv5QKbiXpYOXEM1gaUguELz8YLlewm9JGCgGG/\n9Jer\r\n=ag3R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC5e0b7DCqBFRtwIVpWVAaJLfrJgQtcu1u3zrJpcBLX2wIgdCzB/Ui6C7fwOMLvmebNnhVgfAV/r975oOCLeg01zKA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1589959818.38_1589959828595_0.501987513167321"},"_hasShrinkwrap":false},"1.0.0-canary1590110442.39":{"name":"plumier","version":"1.0.0-canary1590110442.39","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1590110442.39+fe5f73c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fe5f73c429d3ffb6f3dd5ec41ee6dca45ee72e16","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1590110442.39","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-3cl9Q/rB58u8RIqP5d796A1H4kKO/wNluQnLRvwu2OzoyQGTzd/rEsdmWDShQ6TXacTxyh4juCgXeQrJ3v0YQQ==","shasum":"fe90572e0c9b5138fe003f4f1c5dd55dadf4f4e6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1590110442.39.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJexyj1CRA9TVsSAnZWagAA3/oP/1TiYz3jNWgWu1MyE5z5\nqHo0wUjUjHhF925lIhSB+5O1fTSUIa6DRhKf+znKZOx4X+WBPRkR17jpc2wd\n2tHN4MpkQ8KXyHmqyvRkVPtE6Uba/mgAx91VR9TEwOb3XAR3rZzQdF7+sbAE\nLIC9N7MRQxN2R/er296vAufLsOmufGHIPH6yiub1ogNxdLFD4MWWy/5SzYkJ\ncatZXhOnVy4hRrasHVWa7FV2/6JmNhO1jrcoGumgZiiaBWktAN2mUeED3eYM\nPa4dTSRGMi5d3St1x6qheqTgOdvKkRnoLO0f5BR0oXW9R/YQ75dMiFUcWev7\nOXw1UMDkMjI8G3xwmL+ys9u9hEN7Ennal5mYryT6DnH2NigObxPzFGYMtQxB\nWdYL3gQH+qTEC4dIpxicK/lwwx5mtSoFqDkgXztq1eQv1SXmm/SZ8q3P+VtU\nETFfdjXFSIPiJwSKn+cbkdt+LpPuey7Se4y6/OQ8wZOL79ssThr/TxIqFGYE\nJFslPw2maAWwCnrTNGaLn+RMHy92/Vpv0VjWyoWE4L1P42sq6CSJYQlTbvv/\nDYZLpiap3dbBoC52XpbWbfW0cLeUXLVi0u2JZEyDeEWFH7AdkrjsulbsrUep\nyCe2Me1XYuDjNXT3s3XhlQuT4lwHZDu3GXjHnhHyX2Lu5RBbGD6UysQURyMU\nKM1z\r\n=mk0Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHOU9svjHX/CdEQT9m47wwLXsEUdqQPFbcnNM266Y2fdAiBrT3t0xtjg43HH0OJcWerTuWmYRV71Ybw/SaOkw+gMgQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1590110442.39_1590110452997_0.8698648536298916"},"_hasShrinkwrap":false},"1.0.0-canary1590187442.40":{"name":"plumier","version":"1.0.0-canary1590187442.40","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1590187442.40+c4306f9","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c4306f937758628fe1d03dc18398ebc7f63b6f2d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1590187442.40","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-4E6v5kk4EhrsxeAfGW98k96B97meqYsJa5I3GZ9eOSBS41n5/LkRT0ijpKu1hUakvX+R3C4xBMclXMTHsDnvmA==","shasum":"4b05020b2cb8707e096d82e680beb5218af0c9b5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1590187442.40.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeyFW9CRA9TVsSAnZWagAAUfYQAJ4cMk+EFyKj1uwkmAOf\npv17+OCmXsuWUXO/4DDgDawq52cRTLMCdge/ztCRjH+yDM8wXLQ+4LSherCq\nIskJTdFgdqzIUUIW4LHLU9inOoNyOaHapKduOgDSGX1RH5nFD11hGtcgm/sh\nwwLpniReX9RlNiwOOGG1XehZK8QJgOyT0yBuDx2CxVZpXmdt1jeT7gCdEhcA\nFr9hP1xEJdSCuEl/tf7QLOcRQFxzLUyjvOBM4cgsi6s5YY5S1Go+e5XZtDm6\nqWYKu8lc7+S7+ihewBlQRTVjd8IuVhnspEFLu1gUwjDkv82+7bxltz4h5uAb\n7G+WSgUYj88GfjyCza7Allw2TrCMTTA5O72UPYrmi9eEfmXiz//SIfAMore/\n02/ZdogOlfW03kwFIAAVRoyCuHOlICN//ch5aPO4X0xiXSiLjfdeLUPzzZkb\ncPoe3VqHO/QAPzYIMAQj1xXsK4tEVxMWeI5smE5FZiVvXyB4faKXoN7GoRNH\nfdw1VZnyigx/jJ48bj3LK1H0J3BmJN6p7RYuoKilZqteSpKKcBPqLt7aDYoN\nlOk6l1phc5ynts4G1ZkELXgsdH+Be2MfarGLABvjUdmx0u6hpROVIZ/aEbZW\nYjkJtBTW4Aq+1fJD3kfOB8vn2GlL4zy+1TRUIK5YY5kTYPG5iOd9KZF/8el0\nDI6T\r\n=VxuP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDQ3AMllh99tUxzgdxRAfFIr7ZKs3ShnD66DtRlotHpqAiEAiPvEcANl+56cuSvSeelWdGdsy+QLLLHYu5w67Ijrgnw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1590187442.40_1590187453358_0.6034575245395495"},"_hasShrinkwrap":false},"1.0.0-canary1590210208.41":{"name":"plumier","version":"1.0.0-canary1590210208.41","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1590210208.41+59b3d15","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"59b3d1527a64f53243da78b70457cfdcc8106ff1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1590210208.41","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.21.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-u1vSsiIHlTL7jw7GmY5+3TEdKZH6iCybauAAa79hU+2UlPRP/7YZ2T2LJi0ZrV0nWWNAWOV+Z0YH83VkUSPe/A==","shasum":"725ce439de8ad4b3d3e5dc460fa7baeb3f2e1f3a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1590210208.41.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeyK6sCRA9TVsSAnZWagAA+HEP/RDYyxvwz3jTa9Av4YYL\nmqwTiyJgiqug56qPOClmO1Qi8BYVJ93J7YMeOoRzmU/mQl806Q5YZcn5Det2\niKt2wu+P3Joy3AkURNwlQYaXgxHGI1nbU+o+S/yhjK58fc0C3cch93uGienr\nO3fGSkq9zAHK/uR3YVFybt0KB4HKpPYBYHNAFvoTYtY6NTzvKCicLAddT8r1\nYOSn20csEgmIUJq3C8M69vMF9dfjjcT8cG0EQLZn0NAOEua2rqhsuxlV7pXs\npSpx4CxHXm7pd+kH0Cvs2RB4TqOvXEYw/fGmfdkBSFzAtHH0zaC293zV+zyV\nRX2AaauoBAGe9UEFb+6tmPtKysLakoY7Mag+zeg3Oln7JoU+2Fgt0U2YngIV\nSuAySbF0fNFFaVaQpGC+/VL3irhMFTnUIOT2gwComZXdqZeoVeqNmRj3L3ky\nrxP8QomE+vgHb8TY0RpilORZF7J1zD+Xws0jizwarlXJ06ScBCY4D7zoCAU0\n8sF1NsIJRg31la6BK4a3aLfcxElKBma0cPqLS6PYo5zLXhfZY34VdoO1myAC\nC+00uWO6TMxZHjPmfcVnlZ+tlYl72m2v8kzERleGnTOjHliNMOoguPoOThSt\n0B6bncZsa0hFOlY+yABUAtaVleXT8MWVTXFfI8UDG0tWPrvGas/odyqkgZN+\n0ID2\r\n=IgN4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDR9dA0hKS948qEkgDuLKno5GRn+Fppoexe0hg9NkLO6QIhAPCab6O4DLyyM0uyJemjTj276GOnzDt1QsWk2J8F3C3n"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1590210208.41_1590210219529_0.3560967106082362"},"_hasShrinkwrap":false},"1.0.0-canary1590571808.42":{"name":"plumier","version":"1.0.0-canary1590571808.42","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1590571808.42+be4de2d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"be4de2d28ee98b1d5170a3dd0296ab8a5f5594f7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1590571808.42","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-bhuxlQUBc3VZSYtvibjyryqCkIGelx7EH/OnF4dpljOytm+BjjRDq0ErQ7N8NywXcN7z1gB7xXKpeiuh5Z2YPQ==","shasum":"ec7de03f69fd10a3bd268f7569dd52d86fcabf56","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1590571808.42.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJezjMwCRA9TVsSAnZWagAA8JMQAJ6xo41DTbKop2e0ncVn\nIKHNG/wYmiSDZxnLUubvcAdcbNonLmIap/0t+RS0DmZebEP+BMRglZAHTAnn\nAMQv8n4CKcQZaUdOaYGL5pS1Lsga7CJ0aemh6KWeEA/23CDH7W66+IHBNRDn\nXxg0AhEwiqjC7z0q1rASJaJvhFMfGIbqY7buVddcnKk4dCuFfYw+3M26E941\nhGbDKmCuikR4i8OdKZ/pMt8C9/0gi3/aKf6ZiFlCeEBDsGU6CGHaC8c+uzam\n7tZYkd7hRZGYYZyPZzkawrbWPzR11y7ZEXgtz+G/IoHTu1fo7H4gzeLrq7ze\nBO392s9+MBVjaGsKNsl0QqKZiwFoRnUKJTFvIyQIBptsNXMxmBSZ7HEpxP4N\nzsmkisih/S71uTeTzlx+cLAc2uW3N3N4Ygq6crgoiliUpupEwn/yXFfW5YZV\nxTi+QnA7BWTquUVsUzmechhR1ZmRfORsMIufFoJbD3cuKjkxzhLDwOZ5qoPj\nBrLKoiEZ80EnSgIyy00gWDLCqZ1KzqF+EK6H1yJUExVPMBmRxCtkGwazgnjL\nekdTJ1OuymcLzq1A41w+bbdA2KqYAccDAUGAgWV89uD4cmEvJBEACZX46djr\nr/fWe5942KDTsMoghzf/IYs2NrSaxUyfR82JnT53wPzpNqqgdDWk44xjzmOi\nEePy\r\n=YeJw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICgkrFD080IXvw2tDYU6uBg/fgh60v4RHvfvluQ28+k4AiBHcgo894KqKnTaminoxtWJoR/6v3fPbndEPBxsxxe74A=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1590571808.42_1590571823801_0.37312269024345346"},"_hasShrinkwrap":false},"1.0.0-canary1590708371.43":{"name":"plumier","version":"1.0.0-canary1590708371.43","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1590708371.43+7c6d5b8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7c6d5b8d955aeb661e5671c33ad0e25c86faa5cf","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1590708371.43","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-fYRCEYdd09fOyhBMnyEU33MxAoNpAegQlTmSSSEOvLdoKHv4KhJOdw+djCcsght7Zihm/7SysHlpCOop8qtImA==","shasum":"d2df88c143e6eaa134b9f0a1a0c2a0553d3d6187","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1590708371.43.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe0EigCRA9TVsSAnZWagAAvnIP/jEtWJzkagHingtANFdj\nKfuAa6WXOriYSY+Ds8i89ANPiVF1x1y7K7PZdj6KUR3RzglPZx811ltic1b1\nJHLt5vRp+2WYgQ1Pt8wUaDcg17z+B9eya9ajYFwV13RolYbvb8rpF8S0RQEl\nbixOsg23+/erVRl8v0RqzN75g7ctm633aKX9H3QtItbRQBKFMKEQDJZXyaKK\neVR7U84o+4Nm72hP0lyiKd+g2JDekZytA8ryBNcxiJsTf5hzhnfVQ0TIaJy8\nKJSyqCY9c2ZFlooKO7Nj28O3ytUWwq5wtObGGhMjV5UUOEiXkUMVTVxZn4aM\n+B8kD1LcDHbikITfarhTvsOWAWyPtOFVaedBXK1keMv06T8B+C8vN7ze5ipw\nngDkJm7gd2xCpKBRa1yDzwmMGxIuhjmsALW+F9Dm2DAwcnOIUSqUujOLve1Q\nROcer/Gr7pBBnpLSz9WdyUhO/c4m3MRpzbbBkTWhowUD+3i562zmzC5dpzWe\n6ss9DoJg0d1h2ZTJHwAzYByugUt0BGuJgiSdih1aUG5pMAgX4ceksxSxU6Qg\nLP64FlaRS/FJZWwVLJnJrj6CC/3L2Px2IFd3w1y1Aj63JIcewwwZ/tom5xBf\nkAh3cqN4kmjJ0Qqk2vDmWe1iUcCvkdcAfprifOb4ErohM8u52IGBeHQuhzo2\n8Va6\r\n=4/rh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEfobiA8bPWBGkkOJRsM2IfyPjdnpbnb81yEwBHf5jYbAiBkRbAl4fNQLONjjIvlyt7JYakqSTKRMRursun+wn9F8w=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1590708371.43_1590708383953_0.31862648589472187"},"_hasShrinkwrap":false},"1.0.0-canary1590713040.44":{"name":"plumier","version":"1.0.0-canary1590713040.44","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1590713040.44+ac44f93","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ac44f93d2522bc4d3c0fb376d2def374aaaf4be1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1590713040.44","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-LCi09/Fq35tnLwgqnkvWtZ14KIsEgR5a35qUR3Gd+qqQsondspo/1t6G29U5MWSgiTRezj3D0+SHfzb0gyb7cA==","shasum":"9f7b258b0f7a8718c710c086912839f75c677d40","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1590713040.44.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe0FrcCRA9TVsSAnZWagAAGaoP/1Rnwsxu1iquHXjzloai\n6hL54YqaHCQTpsR+sP9xuc8i6ZPwStBqtru0myDRYbE3zcyMD9LpGaYFEdHf\njHrR23kzPnDSRbsJe8BBtOIYGAH/dJfEM3ZF6XZh+uT4IKSxr9hihTr2gMKr\n7Z/Jd7o/C+iQuMWkkmJewnsoo48BKVTlweAj5LyR5hFh8LTpyBCXAzMfeGbl\nG3UxGwcUKaATq8csi/r0MiDpY3EeM03KsZ+lLyFfeC3Y0kUdvoPp+BXKZCeJ\n7aKuOVeAaL/ihlZz1xfWMUCqRsC3TXivRezypA5gFidgGvRTp0wTrhXuimBK\nXPFMQGv8l8gjrGHDSIQTsXqbKO/7+Lhh/GM/hopUdqT6o/DkGbEaZ0H6gXzD\nLyfakhblIN3qW47/Vr25atlBhJBPeqRpGsUwzVsxxTDSG32u2Zr2pG9cp2UH\nzSROMJn+fyPl91hzWZLzjPhqa2sLD0fdNRJcyQKZLVz7lMTPLj8p9OS+0NHf\nYuv87i9iLFN3e2n48SsfFBWboJw997l9gqHpM3TzEx3zyY3xeC54RGb280fU\nDzOYv2K3ixAMFFXvIumDmovDciae29Yx7lVw//3df0vE9a0hrogZHP8rExjs\nUSz89LIcA2T2AiQIFp9sNv3GTI7m2XtIcrok5c+pVKIMLU1Z21ZXC6D2IfA2\n/q1N\r\n=SQzh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCgxoOr1DkdI/xcl+echbvifBjdu5wniqCO7ODpccWTLgIgdsUpkuVCjpTGYYtP7rXUMREMyGudJl8hRIy1ygiQfrM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1590713040.44_1590713051628_0.5940857863109266"},"_hasShrinkwrap":false},"1.0.0-canary1590715866.45":{"name":"plumier","version":"1.0.0-canary1590715866.45","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1590715866.45+cd6ddae","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cd6ddae37abca08140f182fd1e82e10e84ebe2d3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1590715866.45","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-Vf865day+ZWcmmEyaE9Pnp1xqLmp7o2HNJcWpFGqbX0NxQ/Kws+YAMkI5DdXkviOHK+0urdGC37OvtcAZ65nGw==","shasum":"ae0ad39794bd9d62325263493df60435f9c9b477","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1590715866.45.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe0GXnCRA9TVsSAnZWagAAdgYP/jQrIGOrw8Y4Z+KBmbBo\nQa0dEwFQ6u0Fd2EhVtKwzU06wKFP7zojG7mbV2EXBNzbltUo5v2bnEn1e1At\neB9GWTNpywyqEvffLUdfU+G9lG3qb6m3h2G+yux3VQ/Dp1gPGh6hsv67oBnq\nlPBt4DPKdblVEt5qNAlQ4V8QLcrdg1V+lhwncjXWBalaa61tmFsWaP6xOvDO\noLZy7PXnFrzAqCsqnZnVkTkGBt6v5neljysBAutNJsEZGiEgXsS01ZuyCc5M\nE1lFjZ6jDX/tEEJ6zE+wBOvw3kigAB0nao3yO59b9Qvigqp7dG4OP7xUHx6P\nxnVMzFC29RVH7829vnSGEajF9FotobK4TJsJE4AtuozBszmC8cqk65TmgM4Q\nxgHhT0IsreSIFKca4WSxXTOopHLYGswcQRSle2emDU8XeG67ILNQz1l4fu55\nkxuJxI7o4cuqpW4V/9CHWENRrAHz5dGncLN3IOIHWKazLFsJlQBbcqPKZydS\nmjIFAD1k2UVX34IdlLXR5HWMNQHtwf8tU4eqsJK8SqoXUs9Ekq6m26smyMQB\nV+0quAPUPfIFftRJB0VWXVx6nVtfpTl9D1ry7g7caq2hhEho8oJeKCAJDKXg\nesGKe9uiYzvmxWMrMHXXadG2BXz00r3OI52i46Eb+vMl3wpxowQegVOKUJHv\nHcwt\r\n=om0L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHg9eRdr/bCZVG7QtJbDqMzMQ23SvYLmns6q0o/hKMN+AiAJfU8a1Y9MH10uz9UnWdhe79xPw2qhuPJOc7Jp97KWIw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1590715866.45_1590715878643_0.7502592652908897"},"_hasShrinkwrap":false},"1.0.0-canary1590834869.46":{"name":"plumier","version":"1.0.0-canary1590834869.46","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1590834869.46+ab02537","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ab02537e67610d8355a7e5de82881ed19466bfea","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1590834869.46","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-wiGUbQsIWp1ng0BviurHFnN9MaUz2+3dLRLDwIiJR0l0Y4ROUw4qodbqvX8Y8PfaQY8q/5PXITD4ZkZJhenSKA==","shasum":"e546accea500ddbd9a75cb0af3a8fd565dd85928","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1590834869.46.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe0jbBCRA9TVsSAnZWagAA5oQP/jxORytdix4A8vA9I3eH\nLYDWfdQUGkWTdK0WKVFu05HB7xA9KSmL+ZfUhdliEALgdNPN4lJv6ghXZHuy\nK25YhTb08TJCGGQnSQtBGhKy/t1iWVW9qMQLWjwuUwwsuTS6uyiHbbxRd4hz\n5bGI6brfmubvBswNaDacjSsK0Ecrx0Ezw95XIP33fX+G/qOa9tcFMGlD+5aP\nzlOyXlX3I+OO4u+cp3l/xC55ShwtF3K74ZiUwe98+T912ZCns59hVdl7KA4D\nW8e0wC52LrVG30YWO0yEK7LqelBE6w2leQspUm9p6IbqQIL9AxYae14bCpBl\n1rWv1OXuNKFtb39cNKdNaeg04Mj0QR396TE9ygWP9v9ciHiD6EpMQisYsFXc\nT+bJmtlt50NyHUiKJI2ZJhc0g4Y8G4xKnz7Oy5r73SjOf56iqEeMKb8gyM4h\nzgTTwLAcnrbDfVwqbaob+ZEMb5PcmuRRFwjII5soILhORnEMzbfgTIg0bRAB\nTcsXowqa8mQigBPLAEkOwH3Tq/+wRIhAOOmAEqRztpFms25aC3vzCgFiCtN9\nY4jICdBofoGgk+7Ijsj7e/qp1a7DIu6r/fE6cex7u46nBRxdxH7KXDsPHLgz\n18fq1GE2Ix/T8tLl5OrVFfWb6eoxkSFE/Q9Hg/VQ7cYa1ZWHOCraf3RpWOID\netJv\r\n=hmVS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDj0HEfjm8TxtKSRB5loxk2+YljCVhM/qefa/Hnb9TlhgIgJbIuaRMgUZRblZrZoLqJcfRmzTKA0/AR+oW8uOOnA5g="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1590834869.46_1590834880990_0.386127721647505"},"_hasShrinkwrap":false},"1.0.0-canary1591129335.47":{"name":"plumier","version":"1.0.0-canary1591129335.47","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591129335.47+c56024e","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c56024e867662ff709ba4a48a45f4d63b932f4b6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591129335.47","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-4RdOHMHZz43tk1IlanwsAujkgn9Bd7Y/pRVedCZXpTplbxTLGNwNHrysnurYhrOgeeYfrXdcYXd5Uf6/dlSCEQ==","shasum":"89508212e2b5c1c5a8e99e4e42f86cee411dd2d6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591129335.47.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe1rUKCRA9TVsSAnZWagAAYKIP/AjIFqgC6yW6axOGy3Pl\nmrlzFsAnfW2ZRP3yRG0vdgL9TsoSrSy6naWZUQaV/wvAVsX4j1mcREY3sPyM\ney+9LhZjW4TDJpy9eBLimurSgMdMkx1WGd4bOBL3FIDh9lH1Kwu0Lux4+UGs\nhUzBsMM7AKHRVLz8KdVrsD28YtK9904wuivWN69UsjAKf2HmY8rmh4nOlc/N\nUuR30HqD3Y6XsOAZNHYUtq/FJ4gY2W3WiaUtScxCFsTyK0o/tSbrDkyHUJKk\nMa9QLKGFeILC+GHcedMnwCvQ2pKEgqZfkqeYptGMTFVXplkpWquY2hA3DeG7\nQIsXnU31gOYY+TMcrRPSDuriD+oPH6PFKEFtElNCfySkDeslzyzD9Fov7UZH\nVsNhjDkrjJtPOjWkkYBWeX3HQf8eqRzVmMQ3k+1NTfcLzctTunLDwh28YoON\nqQ9IX5N3gLms4e/aP33GAVJHy7HuQEoghrnqPMEtb9hxcL43aaZcTI/G4ngu\n1Y/gRkZ0pdOCniPmNFUmDR8CLvTAuwLLobHMiT8Pk4znwXdDTGAbIm9frUOg\naUqv2+CWCCF1bTbHPtCy4bIxc/yZqM2Sx/d5eTEYso+EgdJMN6ie1UEozov8\nHCtMJu6yxejK0r0StAc+V1qcpPv8kPB8hTYIE/udx4moqL7Uou1wjDd9V9d+\nXNtK\r\n=+i3x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFCVi92bB6676iMA0bsIFqz3mFcC5Z+dxn+HTSn030U0AiEAhKKb0eYEFWv3f/9h8RXN7sBLxvQplW0q3RdvAgPKck0="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591129335.47_1591129353898_0.6541451395683999"},"_hasShrinkwrap":false},"1.0.0-canary1591130025.48":{"name":"plumier","version":"1.0.0-canary1591130025.48","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591130025.48+46abb0c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"46abb0c2e95f8cc2709aeb5f3213751ed5ca4eb3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591130025.48","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-/SVtvCQvvp7q0BL2++7xgCJL3CJfS1LbdHxat44D0179VlnDrcjI0kPUJ9GOdmoIxP2pbBMm/sz1aQHvO599iA==","shasum":"337b81400b4a1884e024b1e457465c16cfbf9c26","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591130025.48.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe1rezCRA9TVsSAnZWagAA8UkP/RQYkmP/d8F9d0JPOUGt\nT0Vk375i98m9ULoyJ1b3sUCHpM28KXKtLWdqgpolIHQGMNXJO5/LgX8xTpaT\nE+QwbCNrouWDELPSN5rF3Xm6YlHfEYiLGJxc0dDRQGHlqF2ueZR6dff13VMC\nmc272TlKrQX6Eb5ZNnps6Mg8h43kUp2PZy4Y7obDf5wiAMNvZl8u/AUutmKN\nHybuwg5rOgqzvQg9U7WkhZVyuSjGwYkoD9YNvCToTlKcArOm4grH6vjnFS+G\nZwRxKHjJ/s56mMVG+3quFKlkhvo9qFNFzJsB4JOQNi/PbzKaTmtJGoOUQ9Q3\nxUQDLQu/pZnhDdKSdUJp9X9t6g5hLXZjlFxFni1F2YEqCUYfU2uBeLVQbcPV\nLoVNGVDylCv7MTX+xSOhQajGeS0LGMTzTA/3GuTqcyLq/l7x3xS+oYQnpFME\nyqn5kwhBHasLqLp1PQlLDj3fft5wFuH0QWID0/YUBK9wZDuhgn7gnzc8qgbz\nVWaJfhjGxejWe/tAQwjXni5NAD7za2YEMNaZQhHv5fXdicYIiSCIIqffpomf\nAHe/AFoZcAxFCNYFJ3/J91obBFPuBx8WIPC6eROMm5xYVJNuZ0/E1A01pxYr\nTN7FtqaMQqvh/W7eZ93uHTw0a5MaczVG6zYx8eq2y8PpNdxB30N1+S4dbKYc\nydHB\r\n=TjUi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAkA9pJmZjyRSW6H8Wm8SOxlIPya8g7RWrOoQtX8tFKTAiBzQtyemGLiWsT+tmcU2iwwjCNvOcFQBdRQCsvbzrVkHQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591130025.48_1591130035192_0.43954702540268764"},"_hasShrinkwrap":false},"1.0.0-canary1591157350.49":{"name":"plumier","version":"1.0.0-canary1591157350.49","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591157350.49+f23865c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f23865ca215b3ccd1a5c474de75d81a05800e4ef","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591157350.49","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-xI3BV0FiZ/IXHpe+3npbjwWoaFRWJ4PDhaRHAHHYkajlKlsLzQz8GkXtSmBN7MuXuydOws0svbOloTwsKtk1Kg==","shasum":"ce23251db31e03d3d6bf17e174d37e21d3409223","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591157350.49.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe1yJyCRA9TVsSAnZWagAA4eUQAJBXc77r0RRR8YwA7XHG\nLWv882McoRwNPeMvzEqESdzFxUcC+xnc9aSSDArSMJ9iZxPogynmJm5o05/i\n8z/JI0s81to48nMbbsrfu9MKifXyJI5RUSEekGTjYVMxPfSws3KDQ2X0ZFlE\nWSNCMDCp796YWwiY+d5KdHBjAHG79JuvUYAKL0RW10divUFveTF5PQhQTa7I\nF29oJYAGb/cBGh37nmMXJLcgLYLHKBZulCwuieCiq9Spc0Q0oc+YpF/iapaR\nusqWNCIx4FiJonWCTordb0zplK753jUKr1nK08i/+FYLTScG1RiNS+GAbpeL\niDmzCWp5j2MkfVrQ0uHsFd95SP+qMcxPQtj9t6LsY/HP3DLIjG69hKnlXmo/\n/qdGDLK1MczKHGdOocyjLRPPv7/Mhnbo0YZLV1wimCzqKefFsrnsyk8qdsK3\n91M7yRa7bSmemOmzQBfg4hSY+XQufOy4peg2fX6gWxzU1SVz9aO69wdhN6pk\nr2rJJt2lUY7wLEFVgUiHz5o8TxTd/zhA2DIY4gjh16Fl3htJxPBu/CFqTvzU\newycXkSnfYUhe4P+aVfQ5NCaYe8X9k3VcBJfgp/dBkEDbDWWNofnELYeCfQE\nK+F9cgcOqbr5hXU3V3Sa5+Fp6j7wvv07ZnZ4FN2KAnsiLhcUOd3zI4q6PJfY\nK2ay\r\n=DRs2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFx6ASoMYnDysBzu0UhHmTiOmcdkvtgH0hVahDgZFLr+AiBxZZI25QJQWeeo2CVnRmHDIOICUBocKUcDuGpqpDrkzQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591157350.49_1591157361969_0.2065384398318315"},"_hasShrinkwrap":false},"1.0.0-canary1591175604.50":{"name":"plumier","version":"1.0.0-canary1591175604.50","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591175604.50+315d9a2","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"315d9a2ea3183d8002c6684f8b717da8a7a7a3ff","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591175604.50","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-aWbubGyz5FAcyg/rAVG7XO50VUJPBxBgb2sYT2Ph6bIFKtnDkl/OGPLrzSvRW4qvhA3jH5ix8b04r61F2Yj7RQ==","shasum":"ca47ccb95098270cdc58e0a73cc5671fbc6e4b4a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591175604.50.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe12nBCRA9TVsSAnZWagAAdmoP/1k309AnRXgFwr0MwDDF\nStcv9OiSgqq3IKwt/JK+GnVh3E3Yz33Mxax7AMu2Omg8KSpSljxs04E7sdza\n4z3BatXTIPNICsIVXzTPH9L+9+YgUQ3GN/LhyrIZFrnGow2kFkAZStFJmUEE\nfsAKF7sUZsWtY8fvtu0+ITRUSHi//J47TMMQ7kXeawZOWpKSQLqPmbS3fELm\nTs1iZWmC04U/aVCFWvGo/WN4cinPYGgjm/rFwHgMMXtsa1eGM/hcjZ32+O61\nkzrGUy3oYsvGGSOohjy4M6isVLy2uTLOWtsOw3ek8YljGp4JzLVtBF3YDea7\nTXV5KUgcgOfJHM/uKI1r/sZu1TiX62xuFaOUdzIVFZrra15T8b0jgaZNfH4U\nopu3fWrh5imsP5mINrSrZCKFJqCTcCDoWKfBex3xe7Dcw4vKAMrkjfTK6vSy\nvURiU5yIcR1g4FKjNLSLm1jYpwfUrfFNm0KHhzzX/Jlu6bBFJRHuI4VTcefE\nBN8rwlVMosQw4J6yxOCjARt6KYuiLoSSS/WfV+COlOt5lMikplIW2CFHXp5L\nlB3hpcjX2rFtDVMmbJQ0+1ZWiT2dp7P/8RHTHgUyM9qesn8WI7WL1bFAnnr5\n9hpzlsmo296Ojwanb6GhXRm/H0NCFElhx0dd6iQu8bVVwROCd3d5uu3T+OAD\nOQM6\r\n=E1iN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDltgaevJgew2PnvcHnrL0lZzFdXtNKLvFctnyPBVM4qgIhAMH+Va1VkMoHFGqm0vBlBM3r0EJQGoyWOMS7ZSumX/YF"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591175604.50_1591175617517_0.9584136050407974"},"_hasShrinkwrap":false},"1.0.0-canary1591181937.51":{"name":"plumier","version":"1.0.0-canary1591181937.51","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591181937.51+0d4c03a","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0d4c03a9ce3c66d580ff0c2f8fefe1aa4e7b7be7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591181937.51","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-AZ/pqFs/8t+WYj4qX9odg7psJ7z93TW1ayr2FpnYdBu/jfKqrQn5jc+McTycJEnNcMEFWAmEZy758T0ahf/Wyg==","shasum":"6419db9820986a5e554141e8308bedc39daa4ba3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591181937.51.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe14KACRA9TVsSAnZWagAAlBwP/3H0F/Awl0hjBXDS40RY\n0I+fBgOFf3Zxo/U1JS6yXP3tSmkNzcjgr/t76/oEGjepZyTnOo4CgUHrOh2w\n6SDPPtxWQ6fqWgdkfxTW2fTJgZ+9Z7MwOk8/H/oIaLEGkjlmlppwMR6xhYb8\nBB5ommGszR4MEZBY+QQ2cK0RQD2yC1wM0JHAkbQ7Yv+hGBHJn5IHRzadFbMZ\nQFKhYphmtYmK97WJIsFrzS2ycGRE7muwo6dABYTk7jzVBFhRZqZEVzwewpK0\n1gPLe0ZAxVq3QgC9IdgevMl6xceIffkLG1bmlldQWfYQ9sg/f62ZwmNfzTP0\ne6QwBJK5wOYQno+VRzVhgXufMkTcl0SAA81cmAn//NZz5w4Y7hiMo5eei35W\n7zcH6gGIzTZHrP1+RGoXfAIRRsUPhLvRBfJ0v08x+6RIX/vkq6bpwA+0XnkT\nKYPrSHEV1tC2CZqi0XA04eni2b11aGenBL39ILP6H4fXkDX+xS9cs02H4lwC\njqV4G5dD52aTwmZNmU+z/98Slhj1uveIivU7kTQYFwIdi896M/djzodbXh1c\nOlYA6CTTp+S1t3N/pvcwMqGU89cM3u9TteHHpsmUfOjg6ET+yl4vGOMa4RiO\nb45G7MKPIbyFuGiXTKCeAxHmVKvueBeQb9sL3tVyTGJUIaKn8/+/E2yvMC5v\n85iV\r\n=muGg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFTnawdSfLhILObOzd+prErAYq0jVA0CKZyHnbJbMCP2AiBtxQwm/nF0qsMXlxEQNFJz6ZvnV5MviZsFSo0bJKOFew=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591181937.51_1591181952162_0.9751557673471851"},"_hasShrinkwrap":false},"1.0.0-canary1591242728.52":{"name":"plumier","version":"1.0.0-canary1591242728.52","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591242728.52+9b91c33","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9b91c3370621317900201c8e63d240b410c53dc7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591242728.52","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-iQSXFvX05L9rCUtVZ+40SdSy4yVxhb7iAwcMstYVS4bJ0PI1wscafzZTl+9Rcx/Rd7uz0mU2gpV85rJPH4xEOA==","shasum":"8d6a246c305152bbffa4cd94fc62645ebcaf9e18","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591242728.52.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2G/zCRA9TVsSAnZWagAAN7gQAKPwjExCChKcAJHAZc7p\nShUwNB6SwQewYQJLus+BHWdwM/UQRoipklR96REa8qYAW8ar/neb6PEDveEi\nMaYi62KDlmOaHRvye1yaRPceiGjRolgFbxWW4E59/FiWhViwk/NLbvcN3mAR\nEnqGFA3RnoxGNOSl9Jus2OxCZ6wBNPRHpOV0Y0IVAFvUhB4uRPrCjbSM9gkj\nNXYytUX92WJetQwUbojl9v2hzuYBhkVJVFmF7y6gDdiqFUMumpJGLh4icWXJ\n7gAkqE56L+Rp0Kcmbb7+92roQNVKuTpRZLMcOmh9Qs5Glg9awomM1qdPCiuK\nhSSMI0bYMkjQDufk8q5WjhgPkuGZ5WgOZU62aWXUCIVFraXvg5alXBNZ6QWf\nYydQHzKVReDXZbK+06rlhv2S7dpf82I6peWc8GrbdagSfIWodZqa6VCIrAjU\nOp8NKOBGs++IQbdGfwgjykCyD0W5bpZVBpxnZFFJqlEWEGUFSoKbSKk7+WdE\nLNzwpRw0OBYeN7oBY1K0CJncrKVYLm6B1R7bzMY4w3wXNltphDBLV7+LDqiH\npV7cB+yKLF2H06J3f8ZpDw1lLSVTX0OVOnDc1Uc5ff6JXVHIA0ZfGAzZc0Zj\nqgf77/mowJbJUTql55x8Ckt3ojbDLae7MjVksHva3UM2rw9rL8DnlZpZZ8j0\nvq09\r\n=WtVD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC8NVWPOZZNCqik/Rzx78AUAEcdS7lqSYaK4lueHURyMAIgF3ibBnnri0nKOFgorjjNCwuK4PWfbqUsPoQqBy60BBc="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591242728.52_1591242738875_0.9014478170105764"},"_hasShrinkwrap":false},"1.0.0-canary1591243227.53":{"name":"plumier","version":"1.0.0-canary1591243227.53","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591243227.53+9178ca4","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9178ca4625507b704939cfd30dc247c9486dc10a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591243227.53","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-wi7PpgLEATrMXn2dm6/NDDoqhZgsyL47WpzvPL+B0ixOxKZ+R013zxUZi4F2vovPC3F1zNjQ2l8iCHNFwTVfDg==","shasum":"71fbe3fb016015277b764430a8773dd06a25f041","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591243227.53.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2HHnCRA9TVsSAnZWagAAcfMP/jyNjSsAtVblD/UM9S7+\n+h8vTAU/1IBt5FYPl6QoTQx4pzQcYvqgd2dxC5wDrZ85tEk95l5DK3pYRC6n\nDkl+9zDmlvza+SfM9NgLIaImYR0y/8gOwdmh2zGn3HzKsfJI971mXLRUD7fA\nmmxq9rHBLfqDvFrvTOkAzbV+VABdsgvaXQei41y8Nz3gmugKpgKyz8T+TotD\nyTd0Izi2zCBZ5Sz/j1HRHqv35w/GGCUOrny99UUguzFmI9h7w0atRtZcYPm+\nk9wSXgeWjFVCsasDMOMVXSVro4y++aIALTAfb2R7BiU5m/0D2YSNZPTz7rlW\n3cMwJs+AodPqo7NtPPz8rLQTdt4wfUnTk9tgZK2ld8vAidamGfy3dEncWl5o\ndDFPNth0CcB0Fxu55d4RwnvsNbIUp/OQHi7tUWTODTO/dKzQ/e/OAyD/SRCG\nvycUW/okV8bWhw0+DX25GS77UZuKibtCY8ZoShypbtKmK7nt4PNH0lJBXsfD\nSGQAJS5KfK5iXaisLxYyFUfltAfFMkJfKQM0hr3V3nPRmhjZQfPYtGrqO0gk\nilLYaRFW5gHTqNMBGUTeLBQfZD3qrGE1nuz53fbVzphOVYFX1hZrsjp1IHIz\nmnXrzzaI0CQHeJqtoxVVmqzdabHhC4UYpSvv4Nc81qhaXaH3UTUEV9DxzXpi\njdRY\r\n=gOuf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAP5OMTTmJt+BDbqRcWTYfaKd0xgWVb8iOyinFHAjZ/uAiEA2/ytyGySg7J5rBrk7KM70idCh1iTNAaXqDJdQJWeVtU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591243227.53_1591243239439_0.22789063938805"},"_hasShrinkwrap":false},"1.0.0-canary1591264983.54":{"name":"plumier","version":"1.0.0-canary1591264983.54","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591264983.54+af02a3f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"af02a3f7cef51829844b1099c8945d4f744423d8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591264983.54","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-I+fOQgwzSJz9l6vnmz/o1Lun1BlleNhCWieniblkORYPvZnnRQupFR6gbDULQe3O9i3tj4RCILHS1gpqv+RaWw==","shasum":"5d3c77375faf8ff1f113d70f4495111516997deb","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591264983.54.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2MblCRA9TVsSAnZWagAA3nsP/A5a97zejEkr1v36cFZm\nZTwy3v2TaeewA8fVxFgX8+pn47cjX53Ld+umM41wrQMtwLEhwEIHkMb/BmAP\nSBfwG70IgUXDSdDV84zXBD9fhhtb7yjV8y8R5yyPbzEfT6JDIj5hVHUYGKI1\nQCwZauTfBI7XYyBhW3U2bOxvTFxrmSaEwO9rozICzpqe4OJiKYV8SxsJjkm1\nP3M1g8HXziQeZ6UuCCgsLl0uFACwzl85OKOLgyLSVZAy8X6tQU7P9J/6ScKt\nAsgRgyyuBfSB/vx52f0xFVqILdpbPuQCxClhGN4yXjoM8U5NkSPL940/mrgT\npsgIeXmMujP7zFGbQ1XWNJGqwkiS5w78eV9CWMT3YUQl703HrPPGIFgvNHk5\n1OxbsM8ZwtY7FNtG6DrvZNpxI/I0xxHiFEbSKND6BIIwGw7oytTvnwVahlts\nUr0V5vOsEaxzXIvayOIZc6LzJ5io5p48C44IR3QT3gQMP515P+vg642dhmwg\nMvC0ryT8gcvUBJhelKOy4VuwhhLYCACyQ0wcXDn1PkuhfxUF0lkf36VGS97s\nboLgeosrmOu1xkceZNpw9vwwGxG2+KXfGVQSzTet3Fn1OFl28D6ch/8TXoVY\nAuPb7bkgk2WMBA5VhB2WkQ7+swSBQw/Rdd4w5YjbkHzKtTMZs8b5w2EhUQts\n0RJv\r\n=4Esy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZEqmnwUx+1nYUVd5DrpnNhxungEq5qWd8GNsF0/ki4AIgWRKWmArqjfhT2MUYOwkrgp0oaGePuWLYFQ8fEvmCqGo="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591264983.54_1591264997443_0.012907002631768805"},"_hasShrinkwrap":false},"1.0.0-canary1591318908.55":{"name":"plumier","version":"1.0.0-canary1591318908.55","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591318908.55+7f53bed","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7f53bedef2e9e64c70b4249b79abcdf90f49b338","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591318908.55","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-mabplnKC574J3HME8ltjkG/2ufauwrA1wJjZlFrl9x3PvCtV5JeLWIpuO/euMzBw4WbuhKQmtzsd4VtueIUfHg==","shasum":"f532f8517d4d89aa9148612363e76764a1b11dbd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591318908.55.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2ZmKCRA9TVsSAnZWagAAQEwP/imQdmOmFfCdpv9lTt2G\nKLtO5ERi4Nad5YEnFH5XCBd2B0OQNFigLgLPgFP5ErXE5lH7FQ/TxmpKideF\nAITqhFAyXJh9V9Nam7gutWscFQmEczDZ91tHZwoeXHb3+IUH+fEx6BaCNcO7\nCy/qkWWHsuAMJTfGcohGaUNRmM1YRbWYMh+q3D6YyP4Iin5R5Lv7QipbRZsH\nTGq8N1g6YdaTKCr4W1I25ER6LKkhH5XtBhbq8891Ge7O8trbJ2RUhuU2RyqY\nFYP4wB9dgmStZWhWyEcC16IN8x4kS4jS9UNqqv3cMX8fsTmC5vUs7BdOlIGk\nb+eEFYdvyzTQIpp6mgmFK+Rva8uZZnqXmblPQTVi0YZCdxN8t/pI8Xo9EASC\nEywBq4QMrhCSaom6oekNisNBGdcxPma+YNiL9mKq4rTZHhbPgU4koxd20yCN\nRD3hoADwkq4Iw2B31lAw/ntO/XsTdsSxOmhVPIoJKMAnh1vLg0sDGkpvXN1O\nUegYyCOdUItih99dShdc4wW7hS79i0IEm2zX1F/He495Gqkb+ILu4+BZ8I4U\nhworjCneWTJa+aFZsq4dUrqTG5B5/zJYdMoNhBFvpv5nX0ixPjTm/Z2HbV8v\na+2nS/uhmhPnOM972OSBB6EpWz8Zsc9u/K7n4pSm+Mv2Kk0F/zdF/G2h3Wo0\nkegh\r\n=j5l7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE63H96bUnZbb6+AcKJ09OhSxUODzk4epW09ecto3vvLAiBgbuqQofMwwDX6e8DeLO9OMYmU7v80WyAbzasCkYuleg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591318908.55_1591318921719_0.6254084013583889"},"_hasShrinkwrap":false},"1.0.0-canary1591319342.56":{"name":"plumier","version":"1.0.0-canary1591319342.56","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591319342.56+e65c0bd","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e65c0bd326fdd2723433fd1ad59b2989fd7efa88","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591319342.56","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-V5dcJnEnosDyZs7GYxN9anLfvU0DyK/CJAIiZmUR/IGn3TX26nfSkDgIs74pdt0pYwglIwo2aixXRJhs8a7jIQ==","shasum":"2e9c6647ea80c4d192e71e40561ab1726afe9a58","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591319342.56.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2Zs6CRA9TVsSAnZWagAA8i8QAJQ69wgP0Onqk5U5mUEl\nutEtLX/ZeLF9/cPEUO9GSaaenQhxpv0iflybM10TJtpQiEhxbeBuQDVeJpsD\n0CXaSsHjYgyc4A5dRaK5R9+kE+ZZE8baxCmajzu/TJjCAUZDixj4GpUrFaV9\nwWzCHJvWHpb8vw5ic7eCOqj7f3AhEMXcQUwAzvG5x0r7zM9Pt5BRQWwQbZZ1\nQx2KXS6ztN9BVgLbabRK6zWbq2ryw14GbRrbSY1lCOydq6yD4PiJT33yd90b\nLo3k6UDATioRamv3DMyJesJspxLtl8Sw/rkMfekYki25jvz8K9SqYA447O3j\nCOcHwDJ+ndGy2PBh67MdqMq53SL3YqSa98oTSDcSYBLabK3OhpUlRjYwK/Mm\nn65kXd8ZndnfHybcY4DDpHuZRiJOmy1uNeVeMXwMUiVGwGNFABgOHGQzT4M3\nIGg/JpgXeOWa72dumwULH2HaA9YLZfy0oPIx00SUvBLKTRmMzkhWsjfpGZIA\nZ6Jo1BkxphL4S14U3m33ywex58tNFe6iloHATgFLpYWVjGkC5sde1unz/AiV\nuwKRQkFk7vFMLSZXL9UvR9Jr2+fXd1uYr1dG+ep66ZAFSsorIbq+ikC7PByL\nFsvCuLbfuqwxy1Ob6sm+uk711bQcCob2vX1OQdsEnbkL1XuuXqJHkqM/lYBx\nbEps\r\n=LpS0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDTl3c1yLMYqEpcQQ3fjsWpNoZeNk/H0Qh/t5NdwTbJcAiEA54y6cjW0qmmiDmlhsJ4lM3OTxA8pyzP8kpS3h/RBqD0="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591319342.56_1591319353506_0.0014270387447059552"},"_hasShrinkwrap":false},"1.0.0-canary1591345663.57":{"name":"plumier","version":"1.0.0-canary1591345663.57","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591345663.57+6461398","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6461398ad8fbdb64faca2375bc2777437af4290f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591345663.57","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-rVzTDsJJGgAuPEqJfIjxdMpj3fnCdBWdn3UsFCnngU2zXKGDKlbu4AiIV6Cg2Q1SA8rIEzO1zMsMhGEikP79jQ==","shasum":"1f6561aefefb385b937553dab512c7496e9c61d3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591345663.57.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2gIMCRA9TVsSAnZWagAALvIP/2Cw/HZ8iDyIXE9N8EYv\njFTr+zeVSujggmzZcIQ23Hin53VTfjaAVtBxsT1OuHz9yy8cVf1vI4Jnx6c/\nSJmaK7Ifk1lSgtdKSuZzy3guHFd4Ge/CnPx1+IN+gPp5q+FgxsR5L2zvis9l\nRqYDuMOe07/z/w5IX76aZCySxZJ8JDG7JaxAm5Glg1eWKWcAwIaTYJ/4jig6\nwlXrcfY+URNEgbZtNicIySlq19ennLju0Io1caBhTTuyyvWKkZG/a0UNYwdq\nVlFyaznuMVSG2VsHCGV72JpXWT3OGaZoE7WC2m+5e4RbbliYTzEUnN1AMi5I\n0UgTP93ywb7a/p6pWuQ0kzF4TH0IRjOVP7e1MCUz5CYRHrPg+jw+lc1+xJH/\n6mQjjBDZnoFeTY8Oxad4PYukjvIdyyDCMbHkNeDdnc9+OYEbIkaoBuQHAAr9\nxEromrSVIUcnRRm5BXlcfHEz4fI/b1du8oAw5HNh6YO1NpY4hGFCMTu0VuFP\nxBG4iwSkr7MwPIqmFT5Ql5QMhf9dGVKewP+FnPJZ7S09DBZ1wibSxc0D05aZ\n37mSyOppIQQexVo7WvGwKU91ixzBvGo+iN2OUXtDqNpEAI7BtJC/EwtZ5u15\nhoSa/X/6WKQMc8k+1PQpOMzFzIl1FLqQOCsA6pwv13LrHiZlS+wjMYKGW+sn\ncb+g\r\n=7iOS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBwQIjg/gsc6Y7U7Z0Jy/1prUP0NmjI4WuR73sQk9RrOAiEAgSKrOlpwObWJDs/9ZbghS57uJ7wB2QdMYSTMdSarMBs="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591345663.57_1591345675985_0.8203781211913896"},"_hasShrinkwrap":false},"1.0.0-canary1591350436.58":{"name":"plumier","version":"1.0.0-canary1591350436.58","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591350436.58+bf30b77","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bf30b772a1515cc86c650042204ac5c94845a936","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591350436.58","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-uFIGjAthU4PB0Qp3Oaww7lXJ+PzYTcmw7uZutkT70y4+cdrq2B6bxor+iRu8wDsEFeZYDXrNZj0SyQBllRdlbg==","shasum":"173d9ab32028bc112fd4a90c5bdc173a285e1ff1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591350436.58.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2hSwCRA9TVsSAnZWagAATAAP/A3DexWA05s53+X81kjH\ndxDiB364iG4ACp+RCe5NE1p5vpLV8zrxccWaUwNW2CUlcDsD3T3aNtxWz4TX\nHWQaZJc/5oo4NVXRhUJpSXc0EDLpT3nL3Lrb5t6RfH9mHNdIyEXZBvieSOtn\ndgRT4Hcf+vj6lufSbsTM6LO8yJHXuhsFdcKOhyFYaYAH6abs9PGIe6xA+3DZ\n2ZH1f1KXTky/2Lo9ZWV4r6xP6C7SUq2ToXSVLgP8RQs4aoPA9v6h41CjXZo5\nMlsltiCddmoGqTq8YQQ6T4BHpe3dUOYWBHZ8odNnIrvvRtsx3yizomo4rWW/\nKc9+hTAsSjLW+muOgRQYC4DNeGbnS/mpW1V0/SeOnGgB9WIpdAvRw81T8xuv\n08abMgnOfVQ7nBF8mvwHXBwGI9SuNj7dgHiITk7NCDQ8ypl0QE1akIlmbVsX\nuWe9ozGUL66V8iLGvoPexqSw1fAddDNhieKzqVWvYeFIZlkSQtj/NDD7TMV6\na4enQgP8a4c/ePwIwC+GtVgYo0Kn8hAylMVxzVRbQUOg+OWe/WQqWl4HMt70\nCCV6HlQdkySJxOUGZRVtrn6wbdV7guZjoLzD24+E07o/aPVtyXW6SfAOf4sZ\nVQci7cMX3bk2mh9uvHcSJgVH81LJxKHreGDWyrfqZ6uRLd8G8MW/GT928CeG\nd9Tr\r\n=FSJs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFhZEdEQ9W+tMWswkvu0ooq31rjZCbqSLHqM2vVbMgEwAiEAriLBuhGI5M7LcouDpwnls2m2095GcE445bm0z4MRdp8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591350436.58_1591350447700_0.45774750949628973"},"_hasShrinkwrap":false},"1.0.0-canary1591388762.59":{"name":"plumier","version":"1.0.0-canary1591388762.59","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591388762.59+8674f92","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8674f92d36eb8249bb3c0c93dec0ed3f135820f8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591388762.59","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-aR7pwHEc6YMKHOk+K+jRve9cZSEmsD99AQ1rBwa60VdOGzMPhD6hNTN/yuN+fbUte16P5dRyD0cp5ArwWUDsgA==","shasum":"adcaef77f3b9d9eab412e380024b6005e06f9cc8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591388762.59.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2qpoCRA9TVsSAnZWagAAOy0P/0gLkb7cBP0DxO0havNj\nhsSuG2kYVnIjU0EAfqeE37J/0uOLOB6kmyuU47FYN3pqaUEdSP8UI0BqOCDL\nr0QHJ5D2DG+sUMODVr19LogUlHGWl4Na5YBe8FBwHiUSsJk7Vd650oybq7Hm\nfB8bJ7cOZlCRjREz5yL3hwsR/9gnmwUICvbGpt6RbBzvb3tPub75FEBwntml\n2U3YtH9xdIQk4b0vWEtP9KTZLrCuEDC8/Vnep84oDyAfGctO/kg/oCvA3Wlb\nkN1zrOXUQM4jjEXcPn9N4t13fnHnHrKDi5ma8rVtRdYibr8byoc5OW67ZDK3\nOFP1YewQbzV4CQ/KMf8aHbYwS1jXqVcbErBaDoUkPMOE3W8nu5omuzml4oqg\nnU6uOjN390F+5vWWiaLpbtQANXZGeI6QPwFG2xBMJ2E3EKskaPEzUbpRnB7h\nz9wyGBwY/yww7KHwxiyJqPaLyRqwQnLXl1+DbIbhmetlwCEgCtssVRCTHTmd\nrBtBZCL821GOIvy4zdFs+aYOhhgPUCuL6z2QxDEUzGmrj4prH4G1c2NRThsA\nlew1+oqfPBlh06FoOJVw6+rFvnkNm9d1D6k9n1Da/i6Z+q0QAFTSg35KvrqF\nTTtymaCOAkT0cCLCE9kslLbSYxeFWCOfrU74nVvKTWlFV4VHlUamOnuVU4Xl\nib/I\r\n=1jXM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDDDyW8UF5qQK0MyyPYEkTfjQrinBN8tK6qP0RSPWwZvAIgJZsIv3ekOqEyzH5ZjcobuvsOOeurH0Y8fVs7qd/mGh8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591388762.59_1591388775557_0.9252067286741208"},"_hasShrinkwrap":false},"1.0.0-canary1591390041.60":{"name":"plumier","version":"1.0.0-canary1591390041.60","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591390041.60+049d992","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"049d992074a8a900f25c84ec3203b5004079b2d2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591390041.60","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-P8riuR7E+JgSKbFYiVxoQhIWxxyNFsDECuIQY3x91M/3dDRNTobdSz+1NHKlWcDkcDYgAQZRZ4ohF/yCWhQgVg==","shasum":"bbc1193961b0df87aeb226f33496867c8a2a60a6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591390041.60.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2q9nCRA9TVsSAnZWagAAZTUP/1yPdv7qt9nYvKO9eEx5\nLiyvw9ohK6adJkQLTNwbcX4Ga8FDOyvdjN3NthpGs73m+WOFn18YlLekAGWk\nVOTNClUSA68H9qOPaUMn/dxp6phalRwI3B/zETorvp2TM205GbhiccOw19xR\nYo9vzm8QmK5ycmYjkqP7vzY9E1o80fKTBY7Xg9JUMUCNfKQc8cu6NnXRnpCW\nWc2k3yXizy1DQnb5W/GPyjDAoYDhqM5FZIxSiXSAVcH9ron3aLYqYP+isVGn\nxzyoId38HLnwYARDll0QN8BVoWqsz3UH36Az73D9B3aNqRyvDwGxCmrel3xF\neeVosxLDN8EXz+T+vZV11ZWh2cJgE+slTKoXJ3YfxGSSqJduG4XOqei22V2V\nrR2HKGoD6ICbE9ozGul1hyeL7RDgsR+dfScJvmnSzpFP/9ZEzR7gxVj+qXCb\ny7IAOLTcKqgCFds/uhV1VVV1FXAX06wZ/ldq7dL2COX2qF3o564a+qqhre3Q\n0TaYNpFgnqcAoi/COHwkgeqsCL+xrO1J1xBjM2pjO2nxVkqvqyoYYNG6MAMX\nw3bGIdj5Gf8C8KE31E60OApWD1AkqYmWTEkHfGjUnu5oZIiPqe61M8CR7HGs\nSZ5MkF1XVa8vPcYcCnFulR+fEy4yLYoyc7RsafklV0O3lYU3RmAj2pU8cEBK\nUBcm\r\n=QsCH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFkDv1xGpFdsRoqgJPEME0zxeL/INHzRWdxsz77kD4HOAiBGSgfAkMXfR6OoAA/NHDIPpVO2ZusdC+GUriqpHrkUnQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591390041.60_1591390054865_0.5118876553773037"},"_hasShrinkwrap":false},"1.0.0-canary1591405562.62":{"name":"plumier","version":"1.0.0-canary1591405562.62","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591405562.62+493a4aa","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"493a4aaec7e4fab9835bd14123972a692a6ad96a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591405562.62","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-nqUITUQiHGyZwcHTggve3dH9hE3LcZpYTDY4dgn0TxYoR62TKeHa5oVw+66QGb494fLlM6IUjoDA9Wrji3rizg==","shasum":"32498616c99a7a0846dd0b69e420f4a4702baa7b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591405562.62.tgz","fileCount":13,"unpackedSize":29746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2uwOCRA9TVsSAnZWagAAnP4P/0gZe3TTT9v0BVfHNI1+\nDdxt5scR9kWpUnWD3A07Q9Txig09OSLGZFtDOnmPUUaEFlzK3XhySuVXDETH\n4ravhYVEtzwxciAoG/YIvgu9zuLqcoKvEhN+CCEOoTqqaODL/uO82JVnvIjA\nUK0/ncvJKJPWOJLUv+NlkzXZ7Gs+Q3aKaaoR6iQsimN6cr9dqaIWaixWycpA\nslQwLXdfbRPvTm6DW5TAmS0U0TAqWMByHAO3h7iFJ8YhbVayQjDxemYrfTio\np3nT3D5iKKiYpY4CC0lsk7Cv422rniRYdZ0H/1PscnJkcOFC2K8wyzoRBCTo\n6CN2CmCVnNjjscWkt9aO705/yHQ3PaU444sodye5aqbz/H1yMDBh0my7Ngw3\nGoJ9Bf+qXv4s+GuAoWN3xHHeFIvCvMshHiJ6KfraSq+Od0kNpAsIhivAKwTk\n6A8087jvbGBlOmlku80Y0NE9sTiZ7C4k5zENTNe75rnebGa6c2MMkTj2TJ6K\n3tWRXwYtYEYIhtl5m72h1tFUyYZOLCgenn8LHEnKU1iKbrxQQtNB4GyNlGkp\n+0VhrL/JqctfeQnqvlJpAS1/ezv6rz4Fb9msYD4rSadg9HY/XxIjINPzcxHL\nL/zzSHs9+dLSPbdy54ZiaeIISakveb7YpkUSXBToLRV5BWEnOWTY8VlO34aX\nFPU3\r\n=A3pw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHLEi0twSSDLOsYdgG6NSVpu1quody13Mqx7R11KmHzUAiBppg4Kl65Jqwbtqb1Wa82EvNqMFQJywT2iGOIkitS3rg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591405562.62_1591405579458_0.6445493279044527"},"_hasShrinkwrap":false},"1.0.0-canary1591595391.63":{"name":"plumier","version":"1.0.0-canary1591595391.63","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591595391.63+2ed5bea","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2ed5bea7f4ee2289e1dd859c09d0bbe87c9f17c4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591595391.63","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-aKVAXZXGO4PesREllghLzKk2104UHfQJeRNtL+KcC7siv7zFVCu/FZ2g2muIsQBxulE/+vDpiMuO18zVfjDBLA==","shasum":"e8453d3f56371e0dfa4d9a2fabf746a7529cf916","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591595391.63.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe3dGKCRA9TVsSAnZWagAAqZIQAJnU/dEp7lj4UUCH7g4P\nZLu+KArrh2GbJrj2CFji+9t32KHb3dfiX6d3eQxkUfSuOPy+ybLcN+ertMXR\n2+9lO7ntt8FLvd+yLERHEq4siTLsJVzfNPoqWzVMzMSH4WPsi96/xGMhyo1w\nOGlXNhXc7zJqZMu2VjyR5cgqyol+MEx7e5dGcUGftYzn3lXHN7Z4lrKQKWqf\nVLSNjPlEQCZau34EkGJSNpJkS6Ufncq4NAFYxCHL5hWbu05eMp7dAG8Tmj6k\nXAbaY2RFk4xxIAvOPOi7ZgrsjwUBrDTt0dNjN7J/Pedf9MgYPuhBBSiYPI9y\nsBbAyfxQOm+7sAiXVw36RzXENy74IuiZuGZ1H0qfagb80WPlrYussF58rJdj\nRU8Yo+EwJ+sMyWSXGdG6W3io11sWhPQzYmYU5Kchycik3X2qwFRczW77qWLa\naxstbQtQ1VAn1yo0W7x28RrhjmkI6fWg0UAn/t9DFeYXwV3ppCghXJ6DVOAq\nBZKh/ScyNUEQgyL759x9ZTu7NDfOiDsYkCGB8QYjMoScOt5kRICLoL6B0WPs\n8UTy4YIAjv0hMQEhDVqCCVNOqWvG9Sl5bPXq+V8QD2e+ADc0KmbIj/fvuDT3\nl1qG+kGwxvgrs/W2HBBh7PBUFSKCLo57w2GuZInmwjvpGGqS8d9vEUfkXcOP\nGeCw\r\n=K2aB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBkU9brCV3u8p4FzvNvoy9y95nRKmw768mvopkJpd1ceAiEAtK+br8yV4r+/R4GtHGDvaRlpCNn7TheKhDOyC1Iimy8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591595391.63_1591595402345_0.3245672009370473"},"_hasShrinkwrap":false},"1.0.0-canary1591659885.64":{"name":"plumier","version":"1.0.0-canary1591659885.64","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591659885.64+044cb74","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"044cb74d193e9139a8b464e7579d69849cc5756d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591659885.64","_nodeVersion":"12.16.3","_npmVersion":"lerna/3.22.0/node@v12.16.3+x64 (linux)","dist":{"integrity":"sha512-pRBO9WnzDS3GkKkNWha3pNU8t+hAG1+d+3724ZhKNDshUDGm/xz1rthW0wZ3bkVi0FiGPH+XJ0DvhzgLiQfyyg==","shasum":"c80aca86dfdf491187249fa6bc97db3e2b3c1606","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591659885.64.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe3s15CRA9TVsSAnZWagAA/qAP/AhYf4jQRRYEjAnLYArG\nrOjsKppno92CQE4f2JlPOaZ/L0DIrNBa46HUGyOl4/MNeSlxMMSnxITk+o4h\nOxWymrgCnsfaxY61OvoeAfFQdnOzyetyMR1XvHQdgHPvL3g51AHKVXZVhTi/\nhSntV83QuoSYiqr5GKcjTcy6KLNn8Ga2JFZfUWXM1ZZ5JBoMmbi/sFL1dA18\nE1PdSBBNt5WPdabZLFvsGCaatpT6i8rzvqU2ox2yTKI4Fg9iAa5zR/3vjL1J\nUWvANc3BdvteEQiwpJ7R9FrDdJ4RBtHmY1n5Ys0+uDfpb+4+DozQiN8MfXcs\nIYdf4SybdFKQkwS2CdEFQi6MoueSIchCjqOTMuC+dLEoWnaJuAFFdAv3yWVe\nHLVOeuezr0RBeFqlIRpWvotOk76uXrLp916IIsRUAl3gP4mABytgzd/igXnr\n+IeCMUv75MqLbNfjnSKZ/2yf+k7TA8MlXJsHKRbaX+efAdzgJ/Ok20JviMO/\nEVgoEanJd1k4DtRFxxW1eIukUrYSPwSRXDRp99pgaTTODiac6OA6AIlRj0yi\nYYVhAGOHCPUOQv1zYtYoeb67JWH9e6KQ0Fls6MwgqX+CcQ/q496WhNZKIKVG\ny+l95BWYOWYxK9++JWe8Zt1eg9bZck8u+SVV9rx32xMXeoc3JiIIeLPhQeS0\nOqY+\r\n=LXKG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHFDcvzUGnlLF9gA0XmkTpXI8nDhKApYiEODAH1Cc5cGAiBxj6KbakhRsLfJSAv4/vTfZp0poZ1s35hZ0q0ytVkQPQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591659885.64_1591659896800_0.6050352371352563"},"_hasShrinkwrap":false},"1.0.0-canary1591675687.65":{"name":"plumier","version":"1.0.0-canary1591675687.65","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591675687.65+4e400ec","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4e400ec26fd3c821448aa925410cc88ad3ad7d4a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591675687.65","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-wzOqX+Rxw0jkym0R3fQSv2Gz7I9vQrvojsw4ajkP5vOH6AyWi/aE1hNthNQ2A47rJFqWGYA/py76tcfjqnt87Q==","shasum":"1de7db34d218ae22e7d7e685e69b57d68ec500b4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591675687.65.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe3wszCRA9TVsSAnZWagAAoogP+gINSyEqUM7mjnolvJjA\nmBusfvpxlrL3yUQRrw0bVusLxiCf6rqNzlxOg56aSWOtFsJX2e4aN7S/zAL5\nARDjZlF0E+4OAPjSyEs8jWgNfQ+0jreLjz4UA+1b4+i0KIBah4/01SF0R6OP\nfPInfmTh3hix/VtBbIYfSACFsY43t9zflIYSOtab7KOTL7rXO+HMwAS/ydMG\nHVlre65MBpHxW79YyoupG3Mx1CMz1D4/d7Ve7JJOjniUVsx6Q6aMDHb818g7\n0b0gBELwJv0w8XNuTGVRzCfrruTA7oqzDqgJEaWdBEVpS/9K6yQWYMLecxhO\nWT3EXGSu3P5Y1cS0HZHfulNjEHvxfe6H14+3gUMKF71xhAzWJeyv/oFs2i2f\nxRk7C/mrY3Ddg43+bXMVWzhV910VwuCtu1RQ5JHggaIaL+Z0PATM+uBQnZnn\nWKucnqD8ubLCXAeuftCAsU0yCMGMntyF43m5EJCjEtOYlbovF9NFBkxfliMR\njXCNoaYnhXT8ONo3hF6RT1bJ7a2U6b2u7M+C3nm6Sj1FW+R07aNUVSZZfCiD\nE63IOwRL60+oDw4xU6TjZAN6OUuzqQxCGVt0/V/rIrBSTeupMcFAsqpQNDB5\ntQD4JTh7UR3/NUJuPgpuM5xRiIAl22nyFFGklt/47437o0qHkOgcC17uirUW\nVOeA\r\n=aeZ+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFe0//DRGCQ2Pe2ZBQZF+16KO/HiQ4uJTMdd6JquEWQ2AiEAusoVTn4QPhuNrbKoqg0BNTDG+oQbp7hpFsJ33snITW4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591675687.65_1591675699313_0.8213015046068732"},"_hasShrinkwrap":false},"1.0.0-canary1591741423.66":{"name":"plumier","version":"1.0.0-canary1591741423.66","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591741423.66+82a57bd","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"82a57bd113b73341f38cafb5fe702e5414f47f13","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591741423.66","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-uUINongEcrg0xoSXq23kpXRzUSuHmYxK+rWw3J3NFvXbFR6ozfNBpbnlhIDpCn8zG66JHI4jQXYgwgeHb6oCww==","shasum":"791f2c83fc99f6523b54ae14e0e757652b19a9a0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591741423.66.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe4Av6CRA9TVsSAnZWagAAaRUQAIxh/AUd665znS32J3FE\nTJvl6fm74PhjJjjwwrY24dwO91HE3Nlz6y7T3IOix3bTLdlF3XMC5s38ovuu\nOf7Pe6kSo17I8s1Wtk4/GkG8X/Opy6q661c7PyDxwCZPyB3SNovd0VAKygT5\nqWLYgObbOBmTTOx7DZOjWDnAtmnDQsGuH6JjXmhdSctTgzZXSU9AAlVofDSI\nyT3GRuglaETtbrGAGOY5i7TlQbH5kPGvsU/FboxqhmOwB8KijruBtvKUX978\njfg8gGM/2rsKMQBI0rj7ZZtufUx+7bctY6/nRq0F/Yz95ty+cdhHpCpqsvIY\nnjvK4T76cfAEAjh70x4DKDq+KKgnisKcpxUdK0B1AKFd3ac9fZvq35+bFNJu\nlQlHNgtE1MPF3Xzb+mEZkAsrqsUeOLB8/cuVdjT1UUdbW2rDIRuMxDc87XsE\nuivXqwcsCDJLSWYDEzhfycva88qiTlPdDVqLrc+DOnFjB06ksZIGBmnl1Kko\nnEo4Hr4vtI0xRIVYhMsQH2+x6GQ7Wkrx0KCxw4UG0DCXAJ4Q0/Np9GAx/NDJ\nK5pwh8bo5+92BDgpToC5URJvf01P3/SNS1LCS4k1c+VTvBYkZ0y+JLnyzBwT\nrZbatRJJdHURpcFjBTn7oA6+93IVlrE5PGpXYRVIF5kqzTVl7qCkoTvjmplP\nh5B3\r\n=lin2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA0uyDXH0MXd6Q61TEsdsm0DDvQcInLMIgqoo1R8cK3kAiEAt+yaCkFGqozUe/smYlXDMm8UzzNQ8hWqgr4eqf/DBwM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591741423.66_1591741434499_0.4898854657969245"},"_hasShrinkwrap":false},"1.0.0-canary1591786334.67":{"name":"plumier","version":"1.0.0-canary1591786334.67","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591786334.67+c598c21","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c598c219cfa001b2ea916fab8ef4d39eef1939b4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591786334.67","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-hWEt8NLtSwHNqIMB1ieE7CdnnofktEJJWZq8Bc/SJ6MY3JMWgLjOaNCSL3c9gGvzws6rqBsupOVmYkXBap/Neg==","shasum":"23980148210bfc58514568ba1dc33bcd115645b1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591786334.67.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe4LtrCRA9TVsSAnZWagAAwn8P/1TyxTK5ZeCJ0QblMfbJ\nSJ1xsizT9ejg+YFd5LKxlxxeC2eszFFYeM1/Mjqxy7Mxf2ttqAOAp+goSUDY\nbSatsyoe6F+hW2i4qqY5IONbxdpIvJJo8Dghnz4pC2PH66PMP32sIlgEGzHP\n+vJuhOL0ezV7m2VEA9Ci7sAU5UEmHqcSYh6dkCGTR0LNP70TXB+OKLssj8W8\n2dj+a19YDZ3Yk6Vl08a02yzCg/1HA3PqVPC9mFvFVnptFUIFM0UFZbyemTH0\nS2/bl67F5x+Qgz/JGEDlOdvN8sBbElXxpdJrNZWggmeCGGgre7LcGTgclDZN\nSNjbnxap/rR9mdrjbUXipzXCLNypbNjLQNU9S3rmRYMn2FBWM+4QEcY1HRNS\n9Y4aRb9bX+OU9V3Bq254JgUN4LIljJQh7+KoqOs28pvOrcErqcIUq960R6zX\nMjUi9rqMajohq13ginbfqgvvc+UTVFikXKxLVLG160IST2rBYWfmXKvW5vPg\nC5Om5Ax/teg3DPb4Xsk4ry8n/YVStpitdbDSuO4d/mq+IRa85wEp9jHyWPtd\nFeG7JX4qTjLAxZtfHywdUKmV1EUPF/QdZvHX2Fl/Fsj1C0kOF+60j4sFb5uj\ns8O52bgIueS8tfppv5lLE8Uu0rpIDaRsF8q/CosOZkEGRwcZk4VYaN6nok/V\nXgbb\r\n=wOp+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCeAX4HXpA7iPtDbPktAjoI4MY30UJjdIzYHNUWW5c2XAIgJQwA/M1RbaWcRr+gvbXf/990OeyTGbDuo4t7HQl2eQU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591786334.67_1591786346757_0.5480450512567094"},"_hasShrinkwrap":false},"1.0.0-canary1591824688.68":{"name":"plumier","version":"1.0.0-canary1591824688.68","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591824688.68+87184dc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"87184dc4cf113d62d981d3af7084499ede66f895","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591824688.68","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-baKZkKI41PMClhS2Rmkzm1P/hbfUS5Db0uyI33wsTxUIHeab1g3W9dOk2BFz6TrQwlP/W2jocivMGiQti5VB8g==","shasum":"335e4aa0df51496457bdd797e6d8a0efa8726b3a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591824688.68.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe4VFECRA9TVsSAnZWagAAtIUP/1An7FwsIwSEWgyBrjgf\nLOwdCGPN3cUj7GIVj80+LDl09w2oOoGBkqbjYgc9bV8U2FbiWhdJaZYIbMvM\nEH2rKzg2qry/0wSuOESzTpEQ61Qq08RRpQhcumuXRPaSXZAEEigoVkCtUQ+9\n0om8rJ6FV8fjCYWqOfwh+VJyPm7WE8Zcgal+U0PyWiWYKxSsZBjv+pLBd3Zj\ne5RM5MF599VrANCb+yDcp8ScS2gorxzklPAK2xi2hdKKETauof8doX/S3qEZ\nKBQ3udDBTqEomUoNm5HZs9b2V6GKVy1dEafK+pGQhQcpR7Jtz+gwsEmtdG8f\n6dRT4GGk9OgC6+YBiCa7sKKsrcnRXL2qacfdKI3sUHisRIXi5zHVtIYcepVu\nzxs8ed+b3U/8q/ITruwmJzlPic3vv3j5/LgwsT17UShqV/TabAo0Ai35JB6f\nuk1fUm9BwKuovwSZVf21ss/YxlGwUg58oS8cPUTJ439vQGx+vAPrj0Y5saS3\nKLzpvuiW5Fbzcsl0mfjp+qoU6fjYVeRn7fqG/gzQyYwdfxx/ODTMr53ve7X7\nfpdAD8XfnmBP6hBfFXQO8O1tAPch+BeGEROBBPkegGmnNx/ABxeag0RtsaE6\nnDX16H7/3IyMB95znEL8LH2+Fk6GIhICCdg8m8Abx6KpEYfSLS2h3es76gxN\nJx+g\r\n=qjUc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC36uluQG39DvkdnUgpwr0voX6Lg7XQEelvZ7xN9xTh7gIhAPZ6PappTDudgDtB8f1MX3AJorSQ91jtWCllGTh0eHrU"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591824688.68_1591824707910_0.7371791041438693"},"_hasShrinkwrap":false},"1.0.0-canary1591849306.69":{"name":"plumier","version":"1.0.0-canary1591849306.69","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591849306.69+fec3506","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fec3506c1f5fd3f42d2a09d0ed06c41d150b6571","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591849306.69","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-k8EOTYF3O1aq+FY+s33wq49D/ePaYT898gONJ+ozSM3vBKO0VYe7X53P/DRHMi9YlIP8cR41oCEPv38/6OYTPQ==","shasum":"b13a531324e11ca77599f6677c1469fdaba952d9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591849306.69.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe4bFpCRA9TVsSAnZWagAAYiIP/14JSAwJE1Z9qlCCAQoM\nGYRG/dM/hCLzEw+SlxWwDL3ItVSP2FeObHfdGUY4HRjOFKO1klcuKDv3y08D\nWb99+TiXRDT8YsUvWtxWtN0EOI8umXfQfmO6LadNqbCHvBlE+TF6fm71etUb\nuAhj4aCl0CWTmFuQ9V6114ZLhwkbnFKPuXvz+cpF6Ju6JDGVoaSUPBdZMGUN\nw6QblOfY8rp3IBvNS/8Mvr+SkBiiw8Sh4gPhySmkMkLwOEF7Q8vgnu/wHXrE\nl9XDNZg/cQKhLCRv2mxgGvBEp2nabGD9SOUUI/IRCpFyPbVFbwsx2JeM38N2\nOWNMIXsp2OkHEj68yipgy45c0E1qzGoETtuxEpdbpcE+TLBsMCLZKoja9xwE\nKPYqZSjf0DF2dRrzreeor/sB2fHD+CpU/hLpdOuihwKHzUbT2mqeto7rgOlI\nJLbqej0AFd4s6VNDfrGzdX9dENmCVxe2cpJGhZGv+xiXdYQqUbQRqEp9DVNb\nScI/ypeWeSuTWc8Hi0la/gK3C+8ypw07QBccGh+8Wf08lxj5rdwp3bgWRatH\nywDdGqG/GaJf3ZGoeKke4YQA1svZWzc3XgoyXHbxCh+jS2g3FSc+IK9eb9UA\ncsC9rbYGmpe53efIxYp1F+Oo59WVbDXjUYtwjke+mggD9GZ7ZgMzHr6wZc68\nAs47\r\n=wrIy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDc7qTVwiq+RUKiMJMT2xD4y/TcVzYvgGhfSvhkd29ejgIgSLXSJwGBdFndwwMSuafc3bmJVU5OHd/azTILx8tZ2Ig="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591849306.69_1591849321137_0.5531507327687686"},"_hasShrinkwrap":false},"1.0.0-canary1591912793.70":{"name":"plumier","version":"1.0.0-canary1591912793.70","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1591912793.70+92c3952","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"92c395211d5acd5566f75a2b1f0b684640f81a10","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1591912793.70","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-z/0yYVzPP+F5Nd9fw6O+nr1OQkJomZ5p7XTN5mBWDcWHzn+7cHU7E/MVfSNG1KwL22oZyVD1F9vWUQHVb1CViA==","shasum":"efbcd733f9adca85fb0582e096401bab9bd924f1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1591912793.70.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe4qlnCRA9TVsSAnZWagAAUOcQAIfR6R7Q+mCgBIHl7IDd\nypV4+f4SC6aEo7azGy9sYhCawmDkhB2ucz6ymyw/JHHxXkFBxVPOTmVe5Nix\n+Y75O8HNGw4++MJU8Pm0DezBeo20dNVbbYrpyaPcLQ/TswjJLgDOjrWjWLr+\nji6tN5c/JSORYi+e0xT9yLNR87W4jnbWmh7DzvHmLFN24h02uV3LzTVBwwrv\nGQPXvZiTJOIWogndTzm5ZHWxYDiN8PaHn4Fgg3GWWsyCSwhvS02TDgrIGZ8F\nNQRkoH50VwxOYyg50U9exNU5ha7csTMYNTJ5d9P3sltpoePqJkpNY7C3iG54\nG2wnOjbT45QIwQt5qXfHhy1yZi+nGU7i0mmEyjrmB913SXjpC0rZ/pqtFj/7\nVojrL5NWSB27caMHqPVwX1jFcaLrNPwI8mNemvnganPspeDEe9HGc+Z9n2WG\nN4K0CmeMjrSwrded3XCWVjFA8t0/IMiJlRa1Vux8D3V0QA3UINqCk8zfkYCw\n1Rl/tJiTskBk7gnLPU6R5WqOf2skSpLQACbrhoig/DOOMRiMNruYc/JXvQ+8\nHS2m/w1o0dHLUAoo0iB0VJZ3zawhdezwrtrLemCOZgP1N5QqneJ9Z3kaxfgf\nOOH60UzEnhyjKyvE7PAX/9+v0Q4cclb6UerTUBPouinRZRkFFv9RZR/Tc/D3\nDNrH\r\n=XpvU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxh6d6IxvF0ZYP3kzSRas4e4H4mdPoGs1BBbOu5LJiuQIgHqPMUJkQRlpWjOXsgM4vXFwtYoU98H4imK5aowPB5QI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1591912793.70_1591912806644_0.2995986405819071"},"_hasShrinkwrap":false},"1.0.0-canary1592172852.71":{"name":"plumier","version":"1.0.0-canary1592172852.71","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592172852.71+a289cc1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a289cc16852e1531023cc90c1c2df1a9716dc1e7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592172852.71","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-ev1e3ieEN6xX05YbLkFww13WHANQr/xqVBlvBTqN8J21BKcUPgJmoidu4FTgH8kSeCwIdyUggc2MGyM5q/zPQQ==","shasum":"3a41964cae6bf04a4e83280fdc44aa3ba760e2ba","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592172852.71.tgz","fileCount":13,"unpackedSize":29659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe5qFDCRA9TVsSAnZWagAAKdIP/2o09A+2ABsz9eQANDQy\nVLx5KhqptMwp9f1g4Ix76CXSEmE0rRGQelCkt4FdcCCz01mmqgqkSJVmvq/L\nYQRzqKCqz4b8IE35VTKDFZ23ALgaZtw+Bs+Dg3sjSrw/zSjHY9AA46lXc6Wd\np8mRIVkxtlqObxTjSRLnyVM+xKWd+Zd3kX6fUXBGKAMfORkexBaktU8LcCsL\nbdLEXlRtyUZLRnYeQsuyZoLJGpAvJ6DNIzrdIlgqM06xjjBx4gCSS2j17FM7\n5zhc9sj9scsYR20yu5FdcV/Do1KUlaOscPLDW5wePantsZeMKDEUb/mPDzNt\nYtf7ZK+6j4uMkyOGMOIek2W/L1VdIRB/2fAl8ZPkTg4lXsBTKWHU8MBqaoG3\neVo4L6ppy9Nd3E3zuT6Dsvvd48b+68FMqtCA/B6IJyn8QJZd01Nwgc9yOKJK\nSe0HxhC/Bhk0vZvfVZ49PsbJZnYGlT0CaDQHuDvAH8Kd/5NL+0kGZdXREHF8\n/UOcI4rmpElU6THk8JIlErwoZQaf+yePHHpos/ZT8L6wPa1G4a59XnTa2TWx\nDBHD9YvmPE6cpxSy+NKqJ2hyGp1mu+4Q6DNSdowXXrWOP3WzkRNeB6x+qVfd\n3tUxbMAPrcZNVzNxF0zvSC9PP6lCDl6TO6TELU7pRUmR0cCg2MYSXsn6YYfD\nzXQw\r\n=IJHZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDAAH1aFmGQEpa2vPbRnNmneBOcMOPqOxZtIEnIvCBV0AIhANCXzQ67vJ39NdBpFb3Sn/TmEylXVJWK6Dv1ZSHe3kEe"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592172852.71_1592172867168_0.41184345464890826"},"_hasShrinkwrap":false},"1.0.0-canary1592208003.72":{"name":"plumier","version":"1.0.0-canary1592208003.72","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592208003.72+06ae01e","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"06ae01e85f261ae1b4f7f807ff21566960131a20","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592208003.72","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-1bsAS0spkuuPa0vwbCXm56LWkyZD7rhZPwNsYr8i5lxyXnzxcslQoAtnW3jhPk1RLyLT5gJ02N2VxoeNPhJrhA==","shasum":"e0b9b57d9f08d6e3dccbd9343a939510d6d0d852","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592208003.72.tgz","fileCount":13,"unpackedSize":29657,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe5yqVCRA9TVsSAnZWagAA4qkP/2YGM0V1kPq6SZIf/STP\nK6LOf1vU3t5ky9no7XMbeYmFyQVS3Pzd6YV0hnwRFg1hjeg3d4AmOD7ZGjVI\nx5BVu5myqn4sZ8ojjZK+TXAJJit6Ckf/i0qk5FmDfypuhu8/o+HPoKAWw3Ki\nAwHVCQsJ9pIrwZ5jw/WEJ6UzIG6GLDFyXIxJMM5pzjZx/GvbsjcwKZ0+W5U7\nbsAykoai8NDbW2GG/1E0RW2rquXMf/5oG4YA6NvoA8P1z9/jDgv2idJ9lRQY\n6QnZM3S5knJSZGR5TdCyk1lR4i6rT/GM2WkYWyYizVgm0A//5CtKW4rJc+Zq\nwwpxYiFlKZHuKaK6zoM4ZQa+vP/6QCaUZCZjaTldwkaoY23h/ZUlxrNcT4as\nx5csBNvDbHD15Oc99GworAc73yQOKuuttAGczQcgtj9o/nQNE5qRXz6mlZrP\nIIQpiWBShj6dDxau/ZhoJYVEaNX+GFRFWuGXnTRxMjPdU8Z+CBwCIRFu8JyN\n3PKMxiUHLnPBkNjwbGtU2f1+Z7YUUYKvd6va+aS8lh3NbedDEBLo8gcQBJYN\nvaWkNGShT+9TwktVQfW6OJ4xKnNpUc3N6gk1j4yjKDDV3GEVWByyfpG4apEM\nFMc6iU4nsDXZsgkhg611kg23wZPYu4e/Jho8tnf/uE5MW33PTH8WHuPxNMun\nTu7n\r\n=NXpx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDxhFdxdrYwY9F1VTDTpAPuoUl6+8qG1NpVeb6ha8iDyAiAJuSdN+Tab50rx/gH7VtQLFWUwbmGRRW50LxCnCl7pRg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592208003.72_1592208021169_0.7538618442057561"},"_hasShrinkwrap":false},"1.0.0-canary1592211125.73":{"name":"plumier","version":"1.0.0-canary1592211125.73","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592211125.73+432d768","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"432d768cb303071528c4a50a3186862252ad6e0d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592211125.73","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-v1v3iDnELsnkw8PHlpteVX4q0Jn0EtdGKw1D0Miqj2OXmO6lg8b++SXTLLwMT3gAdkqVw8d28WUa3ZN0tP3Mkg==","shasum":"abbd6e50dfd07220cf8b363a4deea98e45a53685","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592211125.73.tgz","fileCount":13,"unpackedSize":29657,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe5zbCCRA9TVsSAnZWagAAFFMP/RONVwFlLcZ2yQCB0tte\nDt1jctlhkSv8B7pNtH8K9olbJzUvp7lGqTzFFzxTwlOVEMzqsKSOkbMVk/xc\ndKetdTStjsDibyisyGDuOBDW9DT29a8BanEpux7WuV4pYMNQpq3znlnViWit\nijKM2ClxeF6k8DPJiihrV7DBJWiMW51gurYC4QO+wkcHY9RSSTa8acIB+CG/\nHV3DlVfRn5MRPnxCaC+bokDvNnRt2Bh2naSma6icEVJHx6NDy4WbNGZdEtrm\nxcMu7SsQFVa2Gij3OWXQ1piIJJeqUoJon1bhFDp/pHXOCXqCJqSAy5JIZlQV\nYY5pM8cSgUU4TkiJtLVbwh4J8YiiNVvqLSu7MphY5m85YUWEMwuvj/IWz3Hq\nhsSq25T7AL3dq1FIez68NZmLksSeLOVLRlkCzK5ZNY1njFk3U4ctDcQwss0z\ntQ7l680b8WQLHgzS0jjz6KOazK/LesbFqQxnrrDrFfv/1sJMYIFdcuKL4M/z\nN3TznDlAjdnBP513CoCsUtiJf2i+s2RvNLuqGaMss8M0hetLkjRB4I8r8rUt\ncyt6u8aEal5cKWQEnNqteOKQVZxmRYYUl9ypcBEFWLpnWJsR6PY8Cs2l8xFZ\nNT3z6yQvOL8RiU0Gn+NamHeM5WLwWwR21U2QkBlnhODAgs9ZnMyw4it/ztOh\n894o\r\n=UB57\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICqhAP0bwVdC2hTtxn/fs4zeqRxNGNbuJDy3FOFSuM4hAiEAu2qePO88BLMpQVA1M1yDuqNSsepPBbucAVRZwathnOs="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592211125.73_1592211138463_0.5662450883578241"},"_hasShrinkwrap":false},"1.0.0-canary1592212488.74":{"name":"plumier","version":"1.0.0-canary1592212488.74","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592212488.74+f24d7fe","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f24d7fe9b9d39cd9553672c9e17841071832db91","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592212488.74","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-3GNe1CUqoq55jw+ukdWbY4ziVteXpp7DKuxtCf4YK+BFiHuWQEfCfrhlFraLEmVA7N3Cc/46ISXa6fVJFWz4FA==","shasum":"11542ceeb90c203269b193dd64be715216fb0f0e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592212488.74.tgz","fileCount":13,"unpackedSize":29740,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe5zwTCRA9TVsSAnZWagAAx2EP/3S6Kw6eJLbw7YXOc+Tz\ntDFMtBGZGksqXXQLfSMLO5CurByBHwvJ3MMu66asx23VzFE26uXhcTNlEOmA\n6X1PnAH1/9c3k0Wp1uJQsjSAbruLXjOlVZMZ20lRgFeXwzo09kXySh5qOhPa\nykZAhfAdvBbePDDU4TU6krXKO0u+uK12fCUyblUH8DaU+lfsbwY/axsw4Fm1\nboaVOZJ7yPJDD6ml+ueZi4s9p4JSIJ3mjyt1NqhHm6FBK64m3CdY7z8dIURP\n7iiHwH1DNYHt0lBKK3GTLhuPGaBETOoPTrygOPJFvdbJcsEXfYwdwhOmEnY3\n0t5pzwDR4lQ4xLACTySog3TKBuoTtfedYe40LvTyYeSEO+/CUhTZ3RFSthSb\n0/ArM5ZQoUOuzyFj46//4DR3I5LUL9iM//HXjCIJ65l4PzgGb9Jyw4qQCCNM\nLaHgc6OQxu1cYdMhwWBmOA+7tKi6PeSm/4fxnrUn6igLUXspXbFQyTaLBMsc\nWgEvujc5tl19y5GrMjCyMZM2vaJjMafxHbjG1v3dTiufeOPX9cnkmQulqPLA\nRz1ZCO2TRYt7zSWpbvwGQwlarEfpRNWsR9vdrnIotedCzTOxBn7wzQYU8a+m\nSmzao53zWlkbfdNQZpXZb8E/43JaPu7Av/V6WlFN/j+tlqy89cjIPIb5E428\nO6tT\r\n=l0xS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFxdV0iPRwQVUpxlTYz7PDcexsQ/Arhlssa9iQzBtrW1AiAhtBA6EufNwRYzQG8uxvlKzfHbWoEWIa8fSohIRvlPeQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592212488.74_1592212499060_0.9424497275206984"},"_hasShrinkwrap":false},"1.0.0-canary1592225483.75":{"name":"plumier","version":"1.0.0-canary1592225483.75","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592225483.75+b3b6b43","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b3b6b4378c493d3c85113c66cbc21e7ab1936901","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592225483.75","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-MIeKuDRfZeHNj3u9ZxCwmfNLNneylbjuQfb1aFPMuE4fiUfxne633423MqWV/mHNskPyn+RBm7g7RjMY75HBrw==","shasum":"763512c6729b402d8296d2e0f90c0e7aefa7b10e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592225483.75.tgz","fileCount":13,"unpackedSize":29740,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe527XCRA9TVsSAnZWagAAFA4QAIQBMcPbBI66msbxLJwt\noEVcbn/wTSZMj6cB8ti+my3GNDsRhzcDRMeSvD9diWlp4cjkb9KwpKqjjDTK\nzIXs+cUQvpNaQeT3uJovlNg0HCEwmQl/ugivDCyaQnaph3nsh2uU+FmNB+Cn\nQ/V3zbSy36pOPxbCfA1XnyMtse7LCOS/5eGUiGsqgLuSnfRXcyQF4DksM+tr\njlHlJTv9knIKrhJrCI+jhRVmAuhecGr9r8bKYqN5nvCDOx9fBhchFwS6awVI\nuc9papZh7n19oUYY80SJTtelXgWLkP4u1p1BUnS1UiW8JYlfD01PSRR5ZhC/\ni0F5niZC7ZzxSssEvWqbQok/UrpE7BiWc2N5z5XpqA7zj623Y/6KaVW1wtTZ\nQr/scqY+6Z29vK9ymbox2z6HVjBzYVSnige3WHXyED2k1/OQeygjqfwX4hOO\nCnhH45Cp2JQgcDOvkDc9YqK45BRtAAmmnEPam91b7dFBgtN8d7f1MYwT69/I\n+CefXreYnCYMSNQnt1OmF2cuyiPckO4w8tWDegrs+mmqhv98FIBcp0n+JFzQ\nGT9zxf1zZe67waRDZgiYw9FHaJsZvuJb0vt+kvfWz4haN+GkZ6IKX8TPZ70e\ngdfC5GtlwFfKZ8KCv6SFqUnl8nHWIB+ZDtUOg3Gsp1IrIF+DVIpi86ZHhy0j\nzBp5\r\n=+ZO5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDnqIGD912uluOUc7r8QVC0/k0PVKzRCaPGf7if2QXeHQIgHjys+Luf7zmSoaEXvAtR5R00yuoY4ZPGitsPwmTHKwU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592225483.75_1592225495220_0.37391669853844145"},"_hasShrinkwrap":false},"1.0.0-canary1592256667.76":{"name":"plumier","version":"1.0.0-canary1592256667.76","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592256667.76+368ce32","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"368ce326f182a7779a21ffa82a9ad62f95fbcf40","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592256667.76","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-6+YGMQ0yXZCxyqf6QGtli6T9o8KXN2chobcze2Wcr7GTLFrhRCtZD8g8FsBjFNbSGgl5wH3tA7kArzI4fwWceQ==","shasum":"2548e75240793550986eadc0ece9dcf1fc162489","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592256667.76.tgz","fileCount":13,"unpackedSize":29712,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe5+ioCRA9TVsSAnZWagAAdPIP/2xfdscn9Qnj/JWueuxe\n+Z/GuzmGFykiG5j2E0KnceZpll1F8OQf+3++ImE8voz2F3OL49MW0g/cRLzZ\nh8IW73nPpVzP1VMKUbCdnorrS5pPG2yscSsJyTPcuZqqt+5qf/5v3+Hl/Gs1\n0MBe7yHwQjzuGISBrrgUFjNMccVRMBgxkMBiHHdkxd9WKkb+qcf6emt4KF7l\nOqSh1oA8PA9J/xXTT9ueSuSGwZ3qbbQP4c4NjTP032PbQEc4kOSRXJW+VEKb\njJzPiqxt9lYRFrp1xEKZ4CS/K6rwyXUjqZjBFZhmglMt03j+w5AAjZTdA+tF\nUr/titS5tQ5YIOSkloVVD3AakWMwd+KgPQT7b+za/uwAP7hMw2+jlXdLxNiX\nUL/mL3IiSqLVDdNZJBIYCsCe/rUZEN8M3788/NdZcCz3iSmKSxQBZ7g15i2Y\n/gZd180jNnSEO48gRzknL7p1Uau/vBau9IpCEqobX21HaXmQMnfJkM/Xbpe+\n7UHA5AXFYWZPlMjpq+Iyd2aSWrMcxjVpRdvvX43soMXInvAN+hObrjCvVqyn\ncb4MpFJZJnLmZyoTyE0eRjIAwH3weJ6mvLDr6popq4jP5H4GpHLmOf2OEoRX\n7FDElbbIPPeoE4ybcx5RITSBplH4h8dNKP94FE9JRaGubZyDHIskWax2zDgf\nTLqX\r\n=oC1J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFehFJLo0WmleGpqYd1eNKJPSL8eZC4kX2fTYT0+qanUAiEAiOpYmF7kTCAT94JC7B1YMFZtA4H5c1Vr6IH9lRYpFPU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592256667.76_1592256679750_0.9645692157739805"},"_hasShrinkwrap":false},"1.0.0-canary1592271412.77":{"name":"plumier","version":"1.0.0-canary1592271412.77","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592271412.77+f1dc2dc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f1dc2dcec164dfc36a2f216a6434007644f5d617","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592271412.77","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-WYTkOBlRL95vSmk43mqs01jbOg2TgUVwwXNs6gItHulv/y48lLE+9FHg27j4WjZrbhOCT7J4mbozS4zvfUwEuQ==","shasum":"b92307ce7ad879c9643a2cb7c8236983779a11eb","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592271412.77.tgz","fileCount":13,"unpackedSize":29712,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe6CJBCRA9TVsSAnZWagAATxAP/iEGSrXHkZyMflf5ZBLU\nr+w0iYg2JivxYVypGYcyldtWTGMH88uubesRH8ZCZ0PlKVLzKT35VXHreIDA\n+fFHsFG0nrRnBxyoyOsFRBoeOe9tEX2jl0xmCNUODDtD5RWAyY7of3FW+KIH\nXGeIyHlxaHVD3lRVviK1JPeVthW4zMRxtqX+A9WtRFEz6OxwP2k7Bk69vhz9\nrLZEx0RL9lCAQI2XUQd2NOooAy16yylJPaYAmWxLYeI/+RZJqiMuN0HnUljm\nYt4cYNJHPHO6zYujGW1hOa5f1L7J38r+FcDN0eWGtz1B3eFookg5bPRW/k1d\ntkXn4zrRgm5EXWtEypEnGvRej6M10jAQdZK6XG7IBLpyCqaQlYeOhSi4jXuT\n763c5ahc/wgR20kTYf2ybgsmjY77rgYiWBjG34I/4C1v2zI1CyLM12tA97zB\n09KqIC6cbiM/YP5cTlYGlvltJsIMjrshyYrAf4NvpVUlvaLDoa/la+d6msjd\n6+UQy/ZKQkSMagtiIdIpUyz8JeMu27IJFeK+akxi0kCAZCRr6EIrh/OHCTNP\nFruzIxhVl+DERU7sfFy6f3bR4r1ZlNQ6QXLrMm0V8y/M9o8Qo+PZsoTIG7/8\n4UXOFeYbhFBFxtIaa9s8HcCm0KS075dEy3HQ+/ln0OFuSN728ixVmjeb7pDz\nY0DD\r\n=vgUI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAFzbE6n9kYK+9zi3KiutIhXnZNZHds2JCigTBTg4u7cAiA8330BSefwMdRe+hYq/cIGl8PW6aQCdJS4B0ZF3z7YRQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592271412.77_1592271425043_0.22445472593730886"},"_hasShrinkwrap":false},"1.0.0-canary1592655926.78":{"name":"plumier","version":"1.0.0-canary1592655926.78","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592655926.78+764db47","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"764db47080dee62d577ca91b28bfdf9ca3cf073a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592655926.78","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-nIvwJO2L9XQ9Yf03PoNvXj70v3Ep9WqS/HHfrj78JK+fASZ7bQ9N7dVWcM1MUepREoJybM8bUs9BpoDI8o0x6Q==","shasum":"7698774559a65e41c52bfa69f549f602a510aac1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592655926.78.tgz","fileCount":13,"unpackedSize":29712,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe7gBDCRA9TVsSAnZWagAAdaIP/1BZt5dMwLa1pAhYNGsl\nyIomoOPrxhwWUEdnS4n4r3niX+BYeqwwQTp6c4FbGBLcVFqwnsmqhWADGQXR\ne9fZkHOvfZeYx5RLi4R6B1o7L8tDR7G7sAofmovyqm/ijzUlng730AUGnrGT\n6wC7BlR0IvKRtEHavWUFZs3hxZqVGzuvSpaRt/7Nb/whVRQUkUJVqz/r85zn\neBVBv3tJ36sxuqRMxPeVo1h6pl9rbKa3YrSXOJjurvyn9UXkK6y2x++G4pkS\nPadARLhjPClBL6YtS40eVW26yreX9UbtLFFgsRS/kVXGFsWaupPWpb/E/gS9\niXBcMol55gXfMMWgEtEW3j/hLHrUFbBB9AObjgtNPFgGGOIIbCVO0w1wsfTS\nRSSAgk0DBEpg4fi7nRoSotpuLUdidh6gZDt+VX8URj3bHR0MsLN/7ThyyZwb\nMnPvpytDD3N7Ki6VKKUd3zGGajTZUNyB1yBIGul/3yTbITHhiVcS/Ig1Fldm\noPHbLdtEOnhquRqftcuBGLot6RcSwNRMUc83+/S/uXzxu7Ntc4W1p+/HWaVG\ni+KCXFzOwnfBzeNI/G67B9KXtpKKv9xKExvGura9+7zYgX84tQFLB5xeX5VK\n0gC4bewpOMOGZpwbi2fhSaMnIvg8TmxYYaNVKhsGMvjOwM7qCYmGncogrH87\ntmFz\r\n=rVe3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxR8IKVcKnBGP8d3VPFDfyDhfgPojZeAlX2SSsnCVmWwIgPyMpr3OAlSyEOSeXkftwZtOO8f0HyJeRgetDEfanHAU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592655926.78_1592655938740_0.3620730244086747"},"_hasShrinkwrap":false},"1.0.0-canary1592806142.0":{"name":"plumier","version":"1.0.0-canary1592806142.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592806142.0+06cb125","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.12.0","koa-body":"^4.1.3"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"06cb125750ec650428ad9dd81e1646264673dfeb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592806142.0","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.0/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-a+KTM1zaM+wQt8x4uYQQkRfUfBIJep4oBqPkAgMCjvZEkeZWNRJbNKftdVO9CzyrUC+E4Dbvdq/ftR7LXWiXDg==","shasum":"4d3724b7d9764c6c4805d3ae29cbe90c46117509","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592806142.0.tgz","fileCount":13,"unpackedSize":29710,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8EsOCRA9TVsSAnZWagAAJp8QAIeJE8tiONPxJlf0+9df\nc59F7psPp7q+DPLlA6YmnD96agasfFTVwz0A/BNPAsYcNnnXUZvpeKvKpDW3\niH/u6GQa2I4gwdfiagI+j/WZvSKduVhbqvrwNvo6rD2vMOizW9bTtnLifH5L\nsSgYZir9DGx6iCzCVcoCfbkk1WooFFGPzRipJ8tMgINigLBplQ/2Qw/1B3Wh\ncuQzR/Law4+Ajg+Khwy1Tqp+EJO2P/9bju1VflZCBOsK8MIm5Q0X+npaBA8I\nreI2cQiBLEjQfZf9YbYFVz5Z6+6FfTfcRnYAoIPZuqsKpmNPcYDC59nEGxcT\nKTxj1t9feH+qQDucZxlvORLhKsSno4+/CXDxxWdPh8VGRNE6d7DwjP/FpgXh\nzhDz34dj0zWXKYEQuQVhxwWZ6XWoTqvgiZZh1L2z+KPLOcIJRSCGfQHGpOmQ\nubN7Eff65WlmG1ABRT8+ykBM/RxGbaKsEoCMW47j5s9Wkdq30E/0MH4pO4S6\nz0fQ/Zk73gr/Maq5+gtD2kG+qW6yoSRyGmTYRcizG30WxqAnYX+tgpSyZ2ZO\n/AyO6DD+LCOwDoAsLmh/rB1swUvJHTTmkw5fg+IhmgwvyLRzY57lrh62WR3I\nW6WQr3gUcC2AQsav9BAwOlrwQTfxI7Y1yf5jNqpfka24GHw0eL3QJupvy3Bo\nUiNh\r\n=3qdM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDbZk1xYXyT1nATpYnhK6WBKJ3U6IzyzhM9OIXv1GMICAiEAwWsp1Vc4HpSxVO7o8LD0s/flgigJlEpujipsbmcykLo="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592806142.0_1592806157601_0.38486679941666635"},"_hasShrinkwrap":false},"1.0.0-canary1592877058.0":{"name":"plumier","version":"1.0.0-canary1592877058.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592877058.0+786bef1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"786bef1c6466ad189ccf28e811ba21b4732bd9f5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592877058.0","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.1/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-MS8AdrWpvzWw8J7emT59z6z4hHBg5yrbLGaalDch7PjAmP/UfEUtkglVUcs+UFwmQVsBcZhd30aQWsF7Yg8vdg==","shasum":"f356cd0887f03f9715aa753e4e78f8bea6283722","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592877058.0.tgz","fileCount":13,"unpackedSize":29710,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8WAYCRA9TVsSAnZWagAA1iMP/iMPV26SbM+bg5oRUln3\nvbFSU8megI0RKZeZKZfaM2Hxij9K8+kd/esk1Ox2arNSqmGmdQ+GRhuLnhDv\nwMEWjbdhmfGxsXznpvdNe/Hhj13BIS8GdCmvjnX7axuuCCHE14JnfIkETXrj\nBVQQWjc0R0coLYwNoBOejLw5aGL/qI5T2cJ07MUQPcTACuy3AYhqoRVWsSw2\nuekS11EUAh0lwH+XR0ybcGb8BKmmWKHqtGZ4PExusoG/L4HOBeGTx/kZR1oi\n66JJmuCBtqb0lSJiGFsTYNBAPFGlVkUf1jdMCFFOVjFH45V3iuxXyNt5MXmy\nUVL7DWI4bjqsIwbAU54NWvFj9yX887CfZTl/erpC8EBq/0Ji5ltwnTgwsO+S\nqp8X4oV++A6LBGQMHaPoGEOFRulTdNlsvdY+U8QS3l0Msjp0LzhMHOJNOrXR\nJPGbii1ZGWMAddlP/II2TJ+gJYzfGcx5DCq59XF2XQJ76xyeZGDWeYJuaMSv\nbUjmZw696lW+Acf+sJahDBgRyOQVsY9eK43R9oXOLTg1tDmCi0Tj4kBZk8Jd\naemQb9NLAUP4J/jiVptF8PW4hTtxjpkDo5AUjlQP2EaZNCSTbaMDnD3STNoh\n3QJYQ8YoZe2OmR11f/XCTJ/njkoQIyotpnIW/MTi44PUeu9VnU3RLEaz0Gzy\nVquQ\r\n=Pd0/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICZod6b5ysRyzIMVY9cxaqIJzI+RyCpKbAA364wYhw3VAiEAoR8nvt8yU2HG5JC7e7De3vO2jkamToE3DNqfHuJQCD8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592877058.0_1592877079784_0.9010671497811891"},"_hasShrinkwrap":false},"1.0.0-canary1592886862.0":{"name":"plumier","version":"1.0.0-canary1592886862.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1592886862.0+438c2de","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"438c2de2249305176eaf2c2e6e526924781edd48","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1592886862.0","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.1/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-wioaS7BrDI/+J1mh7USDVfnk+e2WL3UFb79xyLBPSVWUSkD8zsP/GRnR8Z2rAUyAUY5QLGZ3XWktWywX+plQUA==","shasum":"b85e920b58921d652e61c557633b1e7ceb09d760","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1592886862.0.tgz","fileCount":13,"unpackedSize":29710,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8YZgCRA9TVsSAnZWagAAUooP/3fqWntMS9b2TBIwDdms\nOh/MTe+prhf9WKJriEUUA/51Iqc4HyqkWjGpxiIox1upYd4ASKVr0Rphj5Yt\n58ufMV5nnLGIw0gB5gKK6ucNYWHs99JABmCshKWqoJ1cyBvIuuQyRq5MRXyf\n+zR3Tfs0ENvvByd5pZKrueiAyMpFYermLUZ4jZk/R3tjvaDagzxPuUX0SY59\nKHCrmPgeDvQsXrGdyz+/tYI6tWKs4jR/EYJeAI8Tddv7m6TWhyW9U3FRog6L\nulJnK16T90mZk3gzyS0OrLfbM6HST3BBt+j08dOlc0wbqb71zRE4+czUBM+B\nKFFrSiPRwymxtkbdafBddIgkJ9MH8ydMZ7Vl3lblPgpTDVvO8FLm9fNNVgwV\nwT1a7V0rpTorqPKd/R0Pp+7xBMFNYJZfqlifhdgrJnYafEPUjYv5pzGscquu\noS6RG8OEg0oPsQmmTEpRM28FAdNwMmQjF2DrnCeLi4URbqyxW3AOcgzCYfyq\nQzRx8OxI4/ZwM6kQbOk5dpw9PqLsk/M7d4hurjoWQHCGqpc7HWOsWoT75xsw\nJrWyCzAnvE1o1ocFXxvbMswKSdSZZLXd9BQv1CT65HGp6R9fNo5JSFd4RZd0\nLr1GakkhS8ioGYDHjb7yOQ9M/ectzZdF27Ai6GIBvjtz1Z87Udv/DmdeNsSE\nuMej\r\n=8qSq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH+QvWIG/PDu95iEqpRYK3U0SOtJ2usMDy+UmBpsA4FYAiBS8g25LNHIFuPKC0aA9pioSq7DJD872bLedmbY7WJLFg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1592886862.0_1592886880057_0.5670071572033448"},"_hasShrinkwrap":false},"1.0.0-canary1593026645.0":{"name":"plumier","version":"1.0.0-canary1593026645.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1593026645.0+7c5b780","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7c5b7808e61a5c8bb3fa768a3083f47a2cc28426","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1593026645.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-9VoQ8WbXSjtPB/PSPS/L6qKKK3ojFrSHde7s03UON50RZNjOUC+8jP3RkKCqDxSfQ+xetMSMsZAyqq0vyYTd0A==","shasum":"4be50134ea8640c617246a84bf99290481948cf6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1593026645.0.tgz","fileCount":13,"unpackedSize":30987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe86hnCRA9TVsSAnZWagAA98wP/1XZ4GX/nJK6U6Ml+8g0\nPcPzVFvbTaCgOrsh70UC/hakwvIfSPrRScRmThvzkfEdrxDBpkELhm57NyGE\nLeUDZq8UHs6vQqG8RjDqgB1oHViLuHpQMS/v+ympUhZbk+JmtYhCUy4X1rpD\no9bVFo+5vj+h0InRhWWfOn9cnvb0XGX6lP5Cz0GiHkpDFP9GhR4BMh2CSA/P\nQVg2MUbBw4aZivlL5Xxh5kEuRhEM1Z+kD3LdT4MIsKKvZ4zvIZNoNf+BfUFt\nu5F0InWsAJRRKi2JN34d35h4FYVDJjb1cNPgdtAsi5mb4dpFRCVj6MKa/3WM\nLtnwZ7HFYbYZWQmtLpS/o8+b70E7arb+HonSalxyyBmFSNkAy18BKp0/gA/y\nR+jyHl+pbmpGtXGbt+PtQ7ZMnRX64zzzCw+nc6M9fy6hUHeS2UWQlW8DuJOr\nShFLB3yzh5i00ACP3b03sBhnDvZvAzC7wbyi7ENj5e4Qx6Xkxf9yWl8ukTky\na1yCFfLeTY8J57ZE6XBxawUszobNu4jnG082NcKdb+7hvcDXrxJqed78woes\nAhMQig0JecC9hOZbfShA57DHjEMAoHKJo3jz59HjEc1kkD42JKAl8qHhXZJJ\n2fObI65QwyAXvfGyYr0Kwb/S+wjRLFihp2RJzN+ysfWbTN53WQ933YJJyl+0\nZJWf\r\n=qOlk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIECtMX0wQl5bTV02C/KAuyb2khATON7gBlziSwOBdN9YAiBfUFh/yNeoasO/XRLwxnxi1AwtOqETy0MhkRdKFSiqiQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1593026645.0_1593026663168_0.03129031830432161"},"_hasShrinkwrap":false},"1.0.0-canary1593069049.0":{"name":"plumier","version":"1.0.0-canary1593069049.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1593069049.0+0d2edd1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0d2edd1c9bd4933a5afb0fbdfe8ffa53cf43ff31","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1593069049.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-j1R3HGLgryJSQ2QXVOBsolVKAfgeEJv7u4iQIm6lBhTpo7VW4lYuzUe6eGNmKh41mOIkzy693PiWR65OQ4R6vA==","shasum":"c5894e531f7c47d9fb405143c8b4c78d1b02eb7b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1593069049.0.tgz","fileCount":13,"unpackedSize":30987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe9E4nCRA9TVsSAnZWagAA/8IP/2DcQYdw6ni0iIXIJJKa\nvvYlvCakrFsM++8YyMKtSoT4QQLH36WbJcOBGIeL1s1bCx2PHCd9HHJ9OtDg\npZ4m614jBvp2HoSu2BUH+xJnk5GNTESAUNzyhC+mlkz4FpTUY4PO2Ufuf40a\nNi5dluwoX/qzArtgjpesIJL/ZLzdTHFuju/5DgUE58xZjzAr0xhrIG5eyIMu\nd7HJzrRkY0q4GLZIDnpWd+YnebR1+cvpk51+bNZZ7+zIH/JW1IhhmRfYt4n7\nH1dkvOCK4sOAojrX995ykXpPOVVLKRWBgXakgcdW2YM5/eyufD/qJEqw7ePd\nCeAvP2/su4I6MPoDtlwberGcckmnZciuyVmmoGcx6F7HT4All9BT0AFUoXnt\nj7KIKdx2a0P5y5LYPNKZBE5fsXjpoi6aFsv1cEQaisjzLmdFqsTlRtW7YowP\nOlp2fq6UDXal/Rn5krlDqXfcOnuwp+rWErpVq6CWYGkeM4f9pO2esXl4vXcd\n26SfbUOeqRIo9EpZq5Xn6GqytFvJu/R/zwZVrEZbPj4SBWh8ze4ihOxUGPzA\niMDORYjA6TlulMk889rf3UdZEvm14HS6qi8/e67ATZk7riyhVnl1ujCCEisg\n6vb/ZDAOF8gmPI9o0aSWKvoWiOtCokp0M8NBPQ3L1Zvn7sV1i7CP9/TORrEu\nzeLr\r\n=fxRo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICq6LKo7QgTMpSI3GpLvdiRf/X9hOc0+gn1zwGr773X1AiADgdZ4jepqcee1qcu38vkuw2QazvohuFoEXhTGqaOUCQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1593069049.0_1593069095458_0.6627629710226652"},"_hasShrinkwrap":false},"1.0.0-canary1593125083.0":{"name":"plumier","version":"1.0.0-canary1593125083.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary1593125083.0+fd9f134","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fd9f134964f69f03a1aeba6b8ee3f8385cd9f5f9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary1593125083.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-PNV5AzCTRmJ7Kyk6lEBqeXucSZtEIinsgaZOgfvT3/S398BoGWRtqbShspOtjYFYhWeQ6A+NAbS5nzDTfjhiLw==","shasum":"6947f85c5a6f71f48c283216a181343f4d2a5480","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary1593125083.0.tgz","fileCount":13,"unpackedSize":30987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe9SjsCRA9TVsSAnZWagAADYYP/RaC6Nd2rBdbc+NbGL2a\nAhJzGi3ed1K1vxr9c3ZroWMHnOoL+zTNloCyh9i+e1wt14fOeB4NVGkRbDxY\nu1RxprRYfZropaanS1q0Zvw4T7D20gAMd9SbaL0cfVRIiBCHEFcsj1gU71YO\nil/T7UJiMUBaMWbGC47tWKcLLUCsd+8PjeNKJxxqK86ZEv3aR9J3EVmS+teu\n/34S6xy9zd4rvzMew78/IEPkkxazPG03jY6UoI6wYc6dEZtuzap1CXvxpD+h\nZImx1JjlXnc7TevEiLgwmEbqP5UXW/lw+Ds4kBlO3MeNeaQTtWXGumjRHtIO\nu5ck5vP8aC3G7OlkRPFjvSun+jzBjMmFf9D00ObWDCpDRW1sPsrnIGdXZcEc\nxl5hiWMNHENSPBKkwfjfIG6mRRMAGntQX0/AwH7zIOKn77V/aTbVaWdLnoMx\ns0038pAzQ7Fr2QpSuClQIYdzBaaqPVq9eGRnK7eQ3KwQmVjwMVLDJqU8Kyyx\nabgPMNQS4jn6/FYpnLznWk4Mwj9fwkuMgWUvW94CgwZ5XF4pxsza5prSBYlA\ni/e4+RbhIyAmaCgSb/AHzT8lbaunB6eI52tf1y8MOCWTyAMfbnNdqsD7PtOw\nDZbFp/hfGL4YRSk3+OYdYQu0bfrzI9SgB68a0kf2t3UGQP39fPYHjH6smEfe\n4251\r\n=XhK4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC+VE1RWcRW2Vlks7GYtzHFvzJKHZMzvfDOovg4nich9AiBd+LBJDnDRqB5ha6sJCEMulf66UcJ9+JKg+m7NoP0JOQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary1593125083.0_1593125099700_0.24232791744309856"},"_hasShrinkwrap":false},"1.0.0-canary.kbwv88am.0":{"name":"plumier","version":"1.0.0-canary.kbwv88am.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kbwv88am.0+078d37d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"078d37d1ba9e002ea2272403880fab107a8c1817","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kbwv88am.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-JgbWhyC++4vs4NOLgtsfUq+Yyf3w7uVXa7SAgL3om7gU+YjFr6gyp7atKLalrGJ0rJ4EJUdHXtLWB//7NDQgPA==","shasum":"caf19fc58683bbc2868801554ecf5e7c308fb150","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kbwv88am.0.tgz","fileCount":13,"unpackedSize":30985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe9okrCRA9TVsSAnZWagAAQooP/19AzR3dX+ICXKqFFN4g\nMIIEZxOYayfmhwzYxa75xszIas6YCgRaIdpbW4777wOg3l8TQgB3zrJU+Nbl\nMzBLqR0avzpMdYL5S9zqugKJJNXJN7YbonghrBAD0032nZ+Fp6AWJbDT21I9\nkuBaAZpG72ML29ZRtJmMIzigUHOR6PfSEyRp6u8awI1RRLWtIpR43iYLjlKa\nUvXW5a972Rgjd10wgfHwoy/PDBgUDtj7SehF+rwcfACnjBYoW/vIHOFDxMQb\nceyuXOXMIzRHg9AhuKSm8Wh27i4fRAmpvF1M9G6iojknCuGuVE4w3LX3T0os\nm4wO9QPxhHqWaUfhVrXHF97KoJHC2m/W0Dz+8puTblCVpE+/f3V1/+9k7epy\nc6eNAkDEUIlHGvIIqrKFxUNJ9RffhvT/X6tZDXY1XMn+6kq0c+Tm9bp/JOVe\n9/J5IwODD+bU+oHoen/uS5QjKZL4ND6E2Rn2ahZ6/+/oe8GmZ42ugZAKPRf3\ngXcY6rjJPrqvdFHnFwJDHwRwXfw2QCJ4TIHJ6EA1ISLqVuQN9eU2HLHwREqY\n50/MBOvFUSI5aKO+Ca4IQ6wsf9347jr4rfGE9Hvw32JGVhJm5L7QXee/El/G\ngKsiTfZb1H/Ns3pZk6EV9VWNOYEmTxGbgipa6fsS06FtLp0alZ3vH+1RYmO8\nVk+g\r\n=KDwW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFcwgh00rxR+fBKSxSHUuafK+LgEBgc4ebjDy810p7QgAiBZKIJgEPkMv8vxTKWEb298ZqoZwSxcvC0BSWLdAPLfzA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kbwv88am.0_1593215274612_0.31591832241518847"},"_hasShrinkwrap":false},"1.0.0-canary.kbwxgwmn.0":{"name":"plumier","version":"1.0.0-canary.kbwxgwmn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kbwxgwmn.0+9e58720","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9e5872096730b4ada056db7bd448b509f7950c67","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kbwxgwmn.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-p5BPp55PSCfXXMUuNmcFCv5EBZf2rDtTkdrCFuW2mWnPU/nV1TmwMa+IKOXSN2jQpkUC3va+ylI5ATtJ2Bzp4Q==","shasum":"7ac87001dc6a4b22c57e6b7a3d87c145c707ff57","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kbwxgwmn.0.tgz","fileCount":13,"unpackedSize":30985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe9pfeCRA9TVsSAnZWagAAFFsP/3vRF59B3HrbVRln401g\noOWQrKR2UlOBYXXxUuZDJBfajYOrm//WQY4SFroF3Nj+3MturcgHrxgpc31f\nO/c+6cDuAFL7KmxVwFdHfUQaYpmRAZMcxc6JAYD1kI/EAGvJrfLwn2KDbtaT\nQGuHuIrC605J/msN9Q1wS16wlrAzsqqzgOpBbh8xC77d+iNfLK8WrqevYJbh\nP2nA7rVpW64pEtDaTC7ZXk58g4099pDFjtezjEQX0vImq0dypLQiDSaSqryc\nkrLOSqf+aNoqdn/xyfxSKGUAg1degZgPHALFCeSPvlivt1tWNJfFCTArMEv7\n8YfCuA8SNeGz9tlWQ6IrnnLkE26YoqrDSpL9SchR6gyykZ4GwQrx9ivMMSSG\nNMUQf8Dnv8ErNVejYFtt29a/r1NJ95txLV4hf2fWbi5DXagMWtuTm0BNX5cY\noip42X6KEOCOE1GuX0NtJpIkKPX6Wt0SytGsy2Z3j9WnfCTFJLaUS5I/kW3n\n5E+Cp1aVxy9jOwA7XFlWXPmW3RinaZMuyEUz+MYcVj3sJnOgzgCB0lfySatp\nq3Z166z2PuNY0pybS08mfg0jsJa1aq65RP+/zfv16ePDbXeZ/kRvv4agcB6x\nrpEHH3h6AgUcHiJ2KDL8UkqQ9xdOst3LHmO393qNi4RhOzOfwbwPuH47AvPi\nXJhy\r\n=Vnqf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCxwILG728ny+Tmxz7jUvwdUhTWCBfsR4+h/jfwF3myuQIhANX8rVxnphqez7BrzEdKHi87s6dZA/R6EEPgeR3cczVF"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kbwxgwmn.0_1593219037842_0.44601876644016714"},"_hasShrinkwrap":false},"1.0.0-canary.kbwy7dnv.0":{"name":"plumier","version":"1.0.0-canary.kbwy7dnv.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kbwy7dnv.0+8340cea","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8340ceac4818b017b05164ff69bcc243f950e96d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kbwy7dnv.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-r8WWiSxi6fKO+KJk1EdFhOlS5CjzzBxaO5y1X28o6dCTx73G3ULMllyMw7bwc5XTpsdZMBne/DkwdPK51UBB1g==","shasum":"07cd39bc02aac71fb0f2df946e55010d70b907e5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kbwy7dnv.0.tgz","fileCount":13,"unpackedSize":30985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe9pyyCRA9TVsSAnZWagAAFKoP/iXljnxNm6lnPqmkRLYU\nPfaqIwQYBLGStFLwF2JuRaHiOZx/Kb4xTk89S7ZDuHt04VK8c7uDonXXfG43\nplIcgniPO8cIu95h7R5nTKMKNsnjbL54+yC+Z/FKEO7PpXVJv7xYQ3CFDStD\nSJ78LLYYlvSfMG9HMOamQw02LsiBslXsp4ttL6Sa6UYHEB+5bAIvOQ6fxtVL\nCzhSbG18crqS2klQL2OLWcAuVZZFiiSTXuWeCff9EDWc7o2MGP/0kJMcB5Gd\nvs/WBt4I+HFrzGxTWbBvK+MnDDr8pM7JeqbNidmDJOEgdn9dvcFtaMAettr4\npRn+u96CHzJqnMpBRCieTeWbobWWFWLk6pqDuT4egyE1PLcROzoGueZCm3ty\n+vHleawAmv/icGugN7PON9ESuwUwWARbM+vZogCY3AdL7fRPMJn9VldZ54Zu\nVyaqeO+AFHjvsCLqpKI65QX2yhKZY64EYE1huGvV0Dq1Awnkn2nwfMu8a8xH\nI3hl2Mpv+kEdzyGteKcN+J4FoIN17uOMmazosBS690U17enlGjke3lLHPbwK\nEsMmic6CZvWIUGbwToxVABzNAYYdhmv6LcpeRtKN6n/mxgwCql1QSeVSElyr\nTvBhw0srit9jLW4GY58car7wlxxWKI4bqbNVAbX7YygYtwazqRh7ttrOZXw6\nFXE4\r\n=wJ7k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDsjNOzIrsnZBDPuBULQipKjctiq5U/6WONuYXKByRpRQIhAMfVOsRp0WEg9d5hAT768kjJyvs6WNy+npGr1hn+bFTe"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kbwy7dnv.0_1593220274273_0.19439681502328843"},"_hasShrinkwrap":false},"1.0.0-canary.kbx8uhbt.0":{"name":"plumier","version":"1.0.0-canary.kbx8uhbt.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kbx8uhbt.0+5c9fb4b","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5c9fb4bbcee4097ea4518f571c46b874f022c260","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kbx8uhbt.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-9TLQzt8osgA0IOFjYKnWA3izzsCbe7CxHfklw6PqSnTfAYX/dW6rDdJFiAAW6tAJ1mLpXMGkm53nPwqKP6vaLw==","shasum":"98265093210e94bbff65750614b5ce3504ec817f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kbx8uhbt.0.tgz","fileCount":13,"unpackedSize":30985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe9uKCCRA9TVsSAnZWagAAqxkP/2Auh7GVGLiX3fWZ+4lh\nCKAPvJ6luwaf6uex/OI4HBMWPUIZaLkKox905czfCtmDvM6F25ZIsrm/urO2\nVngH0XdM4D8LAf04Mfq9wrO+U9e4EZHZT0Zb0H29Vw/fbqiFdCIRvGgUI8iM\nfg4Q6/anW+fYJk/1Cxp8RuUrXCCC9C2zP3dA2Qp8jdMQznOerjE2RauOm0+e\n+iswVdotY1MgQ6z9Co+eJTX5GYlrmRJyVk714gXTVaImdntdRNC1g91di9BA\nym9r/wAtfc76h1A4IzWGo6qBeMwPJR5MsEgafzzomjEVZ/lrHo3yLHi1aMGB\nDkHdCa1v2ctd6ZEzv+lnT9/pYsXJILOJUZR5fVW3JFOq1vUbSXQDudaXB9yS\nEBZM/P5xjJ6Esx7E1BgjiZpWuKv7FaLSHXs5W20T4bFn5ROqdlcLBObGg8qJ\nL8sYi81wtuq/Osid+A/uh/c7hisTllGIaepoFeE5GFWbInro8KQQr15NnwGQ\nlfGzBJqcCU0rt3XTqtn6P1NrOnpkcZ6r6VNQVDvdN3cwluwpMuj1/AwqRn+A\nDwoQq3+SvoIGdCt4Y9h/myUrfy8U7W5YiFuzPA27Nb3dqKi3tFw/jb8hSVWl\nC5imXkql/lHtbBxu6AHFave/NIp/NQkBeOD0Ghwc36Pp2msQ4jRu9ZTgG7Cv\nTKen\r\n=VwSq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFa59yKSEQ3Ukd1S8+iOpAO8xNtPuMj4utbiane0hizgAiBzreYNfBPEHR4hHjc5ydMBgfHuPx1g5pzg5VNzcFXHQQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kbx8uhbt.0_1593238146312_0.25554781287208583"},"_hasShrinkwrap":false},"1.0.0-canary.kby6baek.0":{"name":"plumier","version":"1.0.0-canary.kby6baek.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kby6baek.0+3dcf2e8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3dcf2e82f9270aa8077c641dba77ee30d287b0d5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kby6baek.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-Q45icYgzdzwmEETmMxUHLSEq4h2D5LMRvzwxMUMXTZkS+WzQX38JccqLNYwMRCbJMOxMIfM5YCyOAe13BG+ItA==","shasum":"4423e08aaf25becdeff5b6b49e7a71b458b148ba","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kby6baek.0.tgz","fileCount":13,"unpackedSize":30985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe974YCRA9TVsSAnZWagAAW2oP/0/N+G46EorFB4YtuA+R\n/APwdNlXvh1whTHqPZpXEkLfg/RQnWj77GR6gWwcg/0sI/WVmuKuQEBqrzgn\npqJ5KafU8xwU5BQzuQg0cVBg4KVGR0TkbIhBIrBR7fxn0vK+V12Gcraft9RD\nckol+UJDfIPYBGReSwO87X6iWUA1I7V1FHdWgBlDNyZDB4JCtWtAKaqGRdkh\n05FEdZBfRUhJz++WFUgt3mddo1+NA//HC1s+70yKGcn3grS5i5/1wwk7uRIh\nNe0Kce/OS8H1bA1vWqn0Suns1Iax5sEKCkw6grH9kTxloB+OxZVzovn92SsJ\n4t40+3KCoSEfHABoQjjOIzhfbDfKYLgoKwccXnaK6nOnWXN50w6TU7T9UmDU\nZbXKOCV/S+Mu2VsTPV73p6Rfd7fHJFhrrvRsBne+MCD1jEDNJ1YWvhXKqNo4\n2Kt2v3/h2xH0STSaeVMROb8t6Pw9SAAX0z9vz9jivsodbAFqu47FPG97eDqM\nfGXBu2LKh3IEYswJFdmDwDN/NH/2nJuOnv4sII0vqf3ORpkZ0cN2f97KZSQu\nr05DBUfajAv1HWr/r5cqJpWzH1EkdJ/5JeH+QX7rr5UhVk4JASqeeKAp0XRF\nEU36vTXZoekPbiBr6WX8kHkmqIgINo3jGnC3Ifeyr26egVN4XevjD2B0mvNQ\ndmwZ\r\n=MXr7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGFCe+Du0lPsCeOLpC0kDvFZYHgu+rjFXfwT7yLb/HhbAiBzPrt9RVHciX3oDCAjWgGCAeZ7KPfkNQ0QSKIv0dro4Q=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kby6baek.0_1593294359389_0.649827414527161"},"_hasShrinkwrap":false},"1.0.0-canary.kbyejgob.0":{"name":"plumier","version":"1.0.0-canary.kbyejgob.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kbyejgob.0+cdf586e","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cdf586edf2c62ed689f8b7bdfed8605d6802908f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kbyejgob.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-0bMO5IRq143tgJDe5bOQhgyHWvqR9jxSE17Z6hXF+RbCC8H57aaoFOen8eKee+rD3mpi8By2BHTfWsi+gM5T4w==","shasum":"b3e825dda3bb13c49922ba972d579244bf74603a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kbyejgob.0.tgz","fileCount":13,"unpackedSize":30985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe9/QSCRA9TVsSAnZWagAA/tgP/jh/pMUyUQo4KM6lSTP7\nW7SRClq9xadp/e/d2RtD3rIYqV5ubis6YP8XSf6gRY5CNVBlMV0jLcYZNVLX\n/SjjOsthX0r2DcU2KqICFZ/8iJEhiABsf/U+LalyCwuomILdDri8ErFFdrVv\nbrZB2RQB+/v+mTmv4osf71PFOusL3DHeFIvPnjudFJ5BPshAvuk+EYICAg8b\nlw51a0GCcZOa2zq7W8pH6bCxuzT102/NQyIsxXWjgCiyA9H9fe1cG007MFU1\nlmiItOJ9ONBA/NGi1R7hv1v16Rm8k08KM9xRbGq3n/MgO7SNuFuqsYGMMljz\nttdkDdlGCP5JkOJRMo+CKIB7N0awOvn0uT3njc1PhQf/3Ed+vtqeCfgBo54t\nsqAlIdfjZRAdmG880XAO1F/W/hCLoqxEqpplglZZlk6bn4fhr9c2JGmoyOIG\nwK9jcRPsCz3NXRmhvvxlsjEAf0PCZLnPhFQq7SonU5b0hn/jZx+cVcRizYpx\nUmj1nosmVjfdycLP91GyuSqav7YpsfeO+qW9uAV7Zh+vBZHbwtIIBTWH2dyw\nFE7vVdOw4CVe43t9wjKy/DRtUPymwtnAhWXzA2y5RHcGvkFLixPgRXKi/DvT\nUMuP3kaB9U4FM7zZS8MqHRWBDuwxKSVR5iFwsZLTV/vbW/KYu1yTxE7LOT4F\n7w0Z\r\n=CiW5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHBO4V3+B119ptZzwhjhbrjl1vjTog2Rvpq8Yr60k6gEAiEAwGVirklD62ssSdl1BkXGVkPcqzCq6qk5GwRp5H7V6+U="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kbyejgob.0_1593308177854_0.590790841965954"},"_hasShrinkwrap":false},"1.0.0-canary.kbyvfeer.0":{"name":"plumier","version":"1.0.0-canary.kbyvfeer.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kbyvfeer.0+c34b656","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c34b65684d8325dfdff30fe799cbfdc5583d06de","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kbyvfeer.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-g8o8ZbsI2Lh/tbfWlnti3S+XBnXqdkTT19aZqWkU6XGmpXkJTKGYsVq/4DeMIKhvLYr5zpKh7s4X0xwRCTg/vQ==","shasum":"e2e1166af9a972f4307f443d070e03e73175775d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kbyvfeer.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe+GLgCRA9TVsSAnZWagAAwZoQAIergFWt349ATUw1OUVF\nZ77cwjW3s+D+KXbDZQf2CRhzhvbh0m1M4IUpUfYg/zZE4+nYH9KzPkCzFTB2\nJtAtGQsZrtx5o/1K1bqpPqrSDPHD05VMgedd/gSFGOF7OMazVGs7dgPTE/zN\n5W6ddFf6Kg+Lc6/nnoyLnUHlxGcQJb/CpB/HVQZ7YGujSSPeEDDBRoawEnvt\naEXqo9jUR2q1NU3HksUyQ4wVpH2tg4IO7AwqDiKmIq5mjqdAIdanq34kH8aE\nKYJCgEU7KFLxVwwpdPdRrUAMjGGajs7N2vaCMDQ7OfMuda5TovpU+hm9h88y\n869647IUw9onyDnMPfVQKPLyD2EPqugU0taoxpEZqWzWav8GPjs+CMZXHlhi\nUvgHmXWovplj6y/A0OXCebGgkKPg0x09RPzu1qykH1KhcEwbMWgvsXvuB6NQ\npRiZvD4PP5qPdqUNrbbSIRSiGv6I0cu6FwmmCMnrhYUtKNJjdSlUcYPU0dty\nHXpUnPKRPmCeVs0gGHxnsXBRu3XYzh6vYXsgXouC/2rGMUBth0CWIlQUM1Mu\nYZ0WtXubk0xwoQOmFdirIcooR1vm/6qHXoJGwPpknzhD07Pb3K9ERY49hnU/\n7kNfT9Dw7a/1kctp+DoHCD9yPpoYaq92Id/FVoM5ZLNLtM2Zv3aBrIp7dd4m\nxjIK\r\n=usNw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChbcLPUl8HvzUbFRhd4Q2u41ycgngtlQ8mO0RtBJf9lQIgXpXlHMrBlQmQlkyGuj9O1bq5/uati7wStuEC2Ybpcqs="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kbyvfeer.0_1593336543737_0.6032423108555858"},"_hasShrinkwrap":false},"1.0.0-canary.kc1bv7t9.0":{"name":"plumier","version":"1.0.0-canary.kc1bv7t9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kc1bv7t9.0+8b07711","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8b07711f7c88b02d9a92012c50917c7bbf3f47a0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kc1bv7t9.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-uxUYDm7mNIaGQ8vA/MKFg2eDICO7AYewM+4nQIZgNmVolAE+lLghRNRIrg3KOMOptotM4EUFD4VX+QOjuMtPiw==","shasum":"830165fd89bfe0ce57cc1df5109cdb1bf1d261d0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kc1bv7t9.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe+qcdCRA9TVsSAnZWagAAUcwP/1b3Dl4ZZbDOcX52vOsJ\nP8oz06Jc1f5PJhuOalsG6UYpWgIcXf37kIPLigwxGjUcAcUDzF/Z69QAS3/N\nrcQyNjXw+DLg6V3l+XxKkNA3gE+rMe8NGLZ+e9FOgQXkt42AYK5lQ4WzQWpp\nD9mqyl7/lzjNeAvlO3OuFtuxUSPFXzGeJ5i/svsZiLjPe0TpteNswznCy8RI\nts/SX+1gF+F9jHQmVimBOOC+Hx2I7a4rsfeIchukfDxKT9EEplWJ0q0QOX6A\nz0QW7q5LFocYmnqg+FrGw0pnWEy2NiTJctoHlv3Q6Xa3DkEFzDBKtjgQZatp\nSe1TL9yObBaawI/3YNSozdnCNIaskkh/3BB4QyksjKy0hf/IOJcSm/5eGIC3\nQK1E9Lk4WTbulvxE6k8rsB727g53wKMShQwzWPmvmn3K2yqRbSqhkpgKKQEF\nLXMT5E2RVwaiqpdGmHMRGXfjV/cxT1g/lRBt3DFYVAtnPMwTkGn48q1Gh9dE\nj2uF8JAHUiT+qglN40M7MkU7r2cmdy5AeqLe46P7QezoEiqOTCF61FgIbuMN\nrV0sZIuZyiXAyrUNjmf0jsdyy6+jGSGPxgzG2U5AkubqNMHKY0XcTb+tZ2Pl\nYRSNopLktmHLs6jmmQjL6h10QmL7lhlABY1QZfJqlyIGG7uepGSpgkOwtcnF\ncySu\r\n=eeno\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAOPrcaV5qzi4vlrJM1kzddyeg8IgT+zjPNKP+T/plNeAiEA/rbsSEd854Db9LKX8eTCvqj4SsI/v10hK7tlVWXBnAI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kc1bv7t9.0_1593485085021_0.3789857405018091"},"_hasShrinkwrap":false},"1.0.0-canary.kc1j62zf.0":{"name":"plumier","version":"1.0.0-canary.kc1j62zf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kc1j62zf.0+2f98d37","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2f98d379811b8883be0add0184d0394e516d4099","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kc1j62zf.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-1r74aPCNzhu8uPrhiYSBON1nUHqYicdBXsCvIPZbSB95sOq6bKQHfLwJsHmDHT2nBXG3fEd0dDN/dJcIoYEExg==","shasum":"87456231533a6e2a15a594ab475f86861209e832","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kc1j62zf.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe+tcHCRA9TVsSAnZWagAA2WAP/jG0ELtfAsXEl8sNfzBu\nIi1qV53RwqXjNCOEkrIEPYXjl3m/utwCnuxL7seWGpyArRoHi63MZUJmNSTt\nhfvJw9P40KzE9QmyHl0qLo/QgIzkuPrjirm95NZDMYpkQLx/SUCoLSBtj8+v\nyXuQ1ZaI4kEUEUmU58XDW6LGuKP+0AVClNG9dO1lgrWmRdzNCEG0URy/s7vX\n1DT8/gO/jZ1Pcvvq0JPkgfvXSgu/sxzJF/5QiEN3GSkfunpvqNy1TZIAatn4\ngV12bFl7ZqvfPGi/CmMicCAe1TzXAj3+r8+QaBgVqtNFgQmBA5sDvA6w2v87\nnjB3nIvHOVHtVTcOzVaqL5bWnAXQtNDubNuW0Iz1gOTQd8RkuhgQM54l20e1\nZvRupYtmnaKBwIjrACS8/lVwC3KZJD5qDo45Be2O/NIXqPX6O1lhsVIYBch/\nOvSrz+w79xaDUjgxGjkk6GZNyj6mT0AiaHDBoHzug6ZRhHBhKRgPjJeM6OLX\nJCVeS7fH4q7Ap3EI3vlF2rWafmfIOg1nbG8rK82wI66GGAhPmJruxIiObFnq\nGKYT9uoC4PhHvyC1DF0P9kUYqi1v/afjOUea5TSRvia+OYIHEiEwLXb+rXKB\noceFpSwyMxMIgIGasOq3jxSGx1mV0t51Th5D0TrcjLCHWkcdFY0lSchVDeNy\nh5Yh\r\n=ceSg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC1ROAmvBfhKR459D9cibwJW83HUbwWKqX3zrgO5PyYZQIgF8tbAYzAtXh57tnAFn0IctPnttT7iSOwT4KvJ2XJ3OA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kc1j62zf.0_1593497351496_0.4520640860928875"},"_hasShrinkwrap":false},"1.0.0-canary.kc1pwqwc.0":{"name":"plumier","version":"1.0.0-canary.kc1pwqwc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kc1pwqwc.0+5a00072","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5a00072e784b732caa800ce0ceb07e6264de1384","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kc1pwqwc.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-eMi6v95eiy4BE2xXCTi7XIFmjG/6hNwL3+eQQc7RT06t7AfjUtfE4udOb9ITNFyQVH2gzedK2ckqW1oodCZaOQ==","shasum":"9d3f7b273a287b18cd655f3ef35b5a2c20ea217d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kc1pwqwc.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe+wNCCRA9TVsSAnZWagAAx2oQAJCM3MVzG8cxK0OYELZ1\nG7oAEZlxL6jVQY8taROUBezQgMx9N+oNDswQLj7hkqlL3oe8acX+spHu/Q02\n9AnoGU6obZ/A+4YINLd1aoHzD7b+FErSuKP0JnBNYEtUP4OfhXQw6Esb+NR+\nUAv++82qxUMU7G47eb6hx0o+623S2ejj27yrxMKalNLhezjkqeq3toO8fllV\n8d2zmN1Qk5inlkSBSY0Otcat9U8DHV23nmZFZAiBdtQdrkuXXodZEffw7nfq\nKMxScZwySIraDBV5+YMlGWUbFsO2hKC5USRxAkOkut/KnLTArAf6lv9oaAga\nU/XHSJQV2Kmg3bY6u7WY/YmbFyypIIS+RR7jLw43bkoQI07uPOKWcO2GQmxQ\nfBO5Rl/sseDjp9Fs5Yn9jcf61kLnkcCpX1zyD4/33GLh+ALn1KJ4TEZoEsOG\n/3TClKKnTTXNj8RZ6A6TMJBy/bzg2xK2odWCCtYny21EB2MDfFCwhKjWGPf2\ngo3rHLtAvJz+jtqK+jorhbqqUmp2PK8pRlfX1VqeZQbezg6+CYOoN9hpant6\nK91nZr6KowIxU6coejVg/xOsw5lFZldMQmC6mS3Bk1KqA4oiG4rIaOxLPxvw\nbVcRD0oj1jG0b2LhXDP7lpyYEWgD3q0CqoBG3/TpPHyNIGRDNWa5UxpkLYCf\nqCuJ\r\n=ocO4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD0eBtoTCLzXunkkGX8jJI0wjVVXrfxnem1hl5ia2O3QQIhAOspOKT9S8SAUiNq7LEaHNUnNgpb4t9KReE9nf/5W8mB"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kc1pwqwc.0_1593508673915_0.43220693451330194"},"_hasShrinkwrap":false},"1.0.0-canary.kc6u1s8b.0":{"name":"plumier","version":"1.0.0-canary.kc6u1s8b.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kc6u1s8b.0+adec22f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"adec22f2ad69278c901d066b154114d5e0b6d1fe","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kc6u1s8b.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-pRL0Ngrv1eomaqk8VjgsuCcNZ77TDQ4TCfGdcYu0n+W5MpUaaPlr6SL6+7X0hUyVkSBA46bsKo3n5LyvJTTGBQ==","shasum":"8bb608b3c8730f440344c94e58232eb87ab79cfb","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kc6u1s8b.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe/7ttCRA9TVsSAnZWagAAHs8P/Rq1IeYXtWEgaHRHs3ga\n9qfzf2jm9vsNfhNDad8zn+w+voxLpyHjI0GiARp1ETVlHi71NRBI2RRSn63+\nb5xmyuP8e6+bt5yuuf4ESHsUUb/Az5a+/D2v2nxcVEF5CiEFtupQdFK0COf7\nXUag42Y05aM1CYEnKq68KSapMy96gIljL9xSJwjh0mVjcH6uf5MUm8aEMdhb\ni/X4efiGrIQw5e5t1GzxzVT7L/lAikMudYADuRAYsKbixGZ2gJtdym1Q/E2G\nl/v2YSu1wcNQLET04Q0+9N8s3Iw3oxcasShIPJO938VPQ6TsGX71dp10EoTH\ng+tv1AjjHrr39D7WqMjwX4kT0EwjLWWgi089ClSuNo+hnw2mbWMIBCRLoTVR\nVLciDsGyLwrVNqM6HnIDMGPs8kIYmJBvoX6b3C7YyrSycBVpipYBHa3D2h8I\nluy2B0XBSumlLU+WVSwnT8eyrtoDgkoBGMBWwUgfmYnJJxjPsHRCP4iFuUsd\n5/H6KmSwbYhfBx+G5pSvGfUcj5ec2NJ4+Wcu/Xlr2QKpxzUWydGx/d60N434\n5NOppnG9+ur/seVL7cWxJTObSwGaoxxgXi0RUTU/TsgAJYd+RQ1w4QnKS5Kj\nPLeDWC/K9fGZgVisKuwjXKchlwIh4IymXdl/EnyzUkEhMi+JoHHhinWKHPah\nWMyE\r\n=vemN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPTJ3xrRVcjwI1i/6kxz66oqhavjjte+wnr8eu7QWjWAIgU28hbId7c+bTjCEgsPd+Sng7JNbxdy0/Nj3dXsCYyZ8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kc6u1s8b.0_1593817965046_0.476683621906262"},"_hasShrinkwrap":false},"1.0.0-canary.kc6zqei2.0":{"name":"plumier","version":"1.0.0-canary.kc6zqei2.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kc6zqei2.0+5053ec1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5053ec1594498080b795e4c92800a3b4aa5cf601","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kc6zqei2.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-4AhucD2ffRdV+m8wK7vN8B600PF2R4knbLMO4ND/HO0RtuDADIIdfxtEFsgqgRr0o9AlU1UuIys7TX1m4fXq1Q==","shasum":"952eb473a62dd184eda5b245d94cbc503fb57edc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kc6zqei2.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe/+CuCRA9TVsSAnZWagAAsgUP/34NlUWVw0f7IuGbm3S2\nFZANNeWP7AG1n2gpGwLhZmrWspEQU6RKh64XJlPIcZICptMMSLKAv9oRwkWy\n9qFVg5KHqwE07NYR7r7NKyDtJu2NJysf1/as38WwrYIKFKaBhipfd+tFW/1K\n+EIbwh3hieaaOm3SrYaGrlLkXkO4uZmOdjmzX0sPJyUHTAt3CMQ8pBaJ8w3u\nusi0vFssSLmFWz+voU6hh0aiyipc8ds9jsypmQxFeUuXaXFtdfFyptOs8cH+\npkLlmLPUUVoUN/E29AzOaRrVxjYMawCu9mw5U6jqIZGkl0ukE1WrvbXeuNco\noQAR9ykodT/84BX9zzSyE3z8rN6iRILMijWzNmpwZeUAetU0Ys7VBhq6lO/w\nhJA2tWyKMbtPI6pjZK/zAKEqV8tCGJGh1Tm9Mb7JbKPloBJVsknt3GKITpZ+\nKNlxRmL4bCpxX3J/LTx/OxIbpox+bWOqU3oWHgYSYb4miw3++ASq07njqY0d\nz+OCCPvnhajhJgokHbddjzUfV3SkB+LLJnNWZjAq+nS/3Nq0W66pH+8DjubI\nzDjW8biBTgQp4fsCvl8i2Jk23ckNpQa707IOd/7+EGffCNI8890c0Gp+/5Nw\nbh30OY1CONEiYSA4zzNJj+ULoTjEfqw3zNWw4NaNNpy6VTlm/mGQCE2Hqz08\nz3tQ\r\n=PAXJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoikcWbeCLPoZyZ75W5I+ve7KAE8L9s8h1foehTnZA5gIgFQ6n7DwrAfdNAhWEjgglMJL5r7XHlYOeYTYtAWDm8kA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kc6zqei2.0_1593827501703_0.7526062790110397"},"_hasShrinkwrap":false},"1.0.0-canary.kc7ailpb.0":{"name":"plumier","version":"1.0.0-canary.kc7ailpb.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kc7ailpb.0+05f5749","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"05f574952f8f5ca131844e05d992fa7a2698f3e2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kc7ailpb.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-9ANgX6kXQQ5bJoh+zZYehMEiAcB3M/h1TgaAiyx7KpcNO1CLz4xIEEqrUXcl0gEmc+ZS8MU8WgfZrXEdZzwLAA==","shasum":"146537d3c9c3415d419f4e737fce555308df4cd8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kc7ailpb.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfACdvCRA9TVsSAnZWagAAAXAP/1NgGIu1b0a4Bqft1Bf8\nfqv+mJwncGlZzHqRpC0xO02bkibMTde/8NpZdOyyP1AEdrkQZz6+xKVc7+j4\no3XTPgF145dWz2ZEYBJUZ4mCXIHgHIoxPYfv7Ff5IiTXyIcRMlqbs++O9c8+\n5G8W8/0NUvGK02i7+3K4TiiTCG3vYgLpcqmI7hMpE42Kq5gODLAfj9ZyCSrZ\nqY6ukxe9h6A20x9JX7YsIoiyMNUNT6eGMiCH6QNAK372poNod6vv5lqIO73k\nBHiUl08PtSRQWUzvKGSthhaxG2bxyA3AXnHiinvVk8BEZ1ulG89DgoneqY/S\nUjwCUwaiMfGkMB4O8p6ydE8G/QIQFG3DM6zdLo9sbvrytPSYzNUNfgxqhFbC\nxNfsvrU4cqceLtt3yKXdZqKvzMtFFVfE+JMs0/VOteNWwtw8HhGLzlhpXpCF\nBXisrzu0aG2rrn35GEeAAqRXEutcXRy61tPNVcuMd5J8/7FDSCb9MjkU+sPd\nOfqJKJW3H23ticqONxYztJv3HzDqwaVq9G7Kj98q2TytOsXSSk21j5H6Z2OG\nodV1EFkhJfHtRAPo2SjhImNm30MpZ0UTUKIVZD14x2KrAuvebnmHUghG0h+Z\njgpdEjxHoF0UODQYbq9m79dv19xUTtxcMLu3914KMk1cl6sA7XC27jjOjyfi\n8elO\r\n=NZgr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDPkndc6YXRs25UCRGar2+h2kPYDJTJ7HObiDyWVpCb9AIgBG1SHxm7QDBVl29EDHcOASO8Xi2N6LzXHugx0Sd0wmc="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kc7ailpb.0_1593845614869_0.4361788356384826"},"_hasShrinkwrap":false},"1.0.0-canary.kciolr74.0":{"name":"plumier","version":"1.0.0-canary.kciolr74.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kciolr74.0+f1d046a","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f1d046a0797df68f93a1a502270d0ed075d25736","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kciolr74.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-GNarwM+2pomshQhVFTxiVp1KHRB84VDFL91HCu/6NsktCTTyFjlAjyhiQB+kbwmV3H9F5QHZ4d+LnPCP0zSQtg==","shasum":"c94a7366d32f0b811ce92ca2e60830a9b8581bc6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kciolr74.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfCqoDCRA9TVsSAnZWagAAnSMP/1KJtLXx/jqJtoWa9Fpn\nvqfc47AP8hYB9WRwbbqmrtCJIB/tnRaTEZs5F2rQ45aJicSJs+lUP1Gd3NmO\nw20JGbKlXTwZlwyTiOGYMCkaJ+hPwF/PRrKXwG4RK+UOb+fwYI9AHT8FNDmd\nvkobnvXpDVXRctPCp4BCwkMBb7AT+yHpycAnWJyOnwFsp/4bqwSlpeoB2lQK\nLHlID7E2KsyXotald2cR+TjPAqYRLlBuegG9s42mJEkOQNVWiUgNZZYomVvb\nnW7cEvj3U7DXP0gMztXQLhtDXqHi2Q8tZUgrz7mM+ABk8j37ZafZgmRJcFqd\nxPeEvvtZ6xi/+agO46jGkz0D3+dnUxLq3EKuLzDhu2H6NYymw5F2GFzI1Fze\nCYv7Q0agOJplF5lBiAJHIgF/J9msjf+UPAYcGkE89BkNCV+T4YQaXw08QO8h\nh49hib+K7rGzIirvBkJ6ZqD9639cqJns2eqe9snMb+GKxQDYQQTCTWplaLBR\n3VHYx4CP3JKeK0MdEcGRgnXYhwzcHRA4JODXMiRUJylJO7TbGL1JWMIOOo1o\njLoJnuG7FF9wJqdXAOKJiucyn08q9dKJlbT255J0l7Gi6uAyAArOVA41o2E3\n464jbfJbojsorLQETQ676apT6JZT/B/r6K6i7Joy9zEy5pgILMyJrnYINW+Z\nAoBN\r\n=gtP9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDijQZnMOV9/d6tbyCAoab1rrLYmJbTsIEgJYDpRa3asAIgUC+K+2h54cBsMsRugam/IDyMbp287RIwhxXHLu3vYzY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kciolr74.0_1594534403274_0.4548926239354114"},"_hasShrinkwrap":false},"1.0.0-canary.kckaf5hv.0":{"name":"plumier","version":"1.0.0-canary.kckaf5hv.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kckaf5hv.0+d4e0415","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d4e0415ee441087f122f131b925d59190a0d95df","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kckaf5hv.0","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-3qkXkUDbT4hXVd6ir99BvYC7yhLOs4ih89QBW9675MrP+BO+nc7kwV8zVulZ3sy/53igKTUMrEQhzOODgN/BZQ==","shasum":"03a18268fc2efd223f554e3e2c96ff9d2918a7d4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kckaf5hv.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDCVeCRA9TVsSAnZWagAAHOAQAKEBSaPsYYdOA+Usdj64\nh2Al74vlhlacLDvvs/qF0q+bumwYnNacEgX3jHuzdwPOASxIpDU+ag5HKZ/b\n4Az7iubH+ADzHoeNAc0rdC9adBMu/XXrDbr/mnd85LZhx5ZDv+cN8HW2JnVZ\nfvs2dB2+NHSz8z/P6Hek3haQ69/2dsjRSJtrNGy5vPPE/Ct++cgoy1qroIh8\nzhB+RNAbEYyLz9Wy5DyF7fbpEes1NN4DWbtNRm6DUTo5ln9zIoPAcmMqN1rR\noHshSfWUI/b0tU8Fcdy6UwIoOiOyGGnY47cGsBrOTGmWsqgMYf6jWiLnJL6m\nKPYpz8mADV4ad1DRdwhq2hHBuls1gVfm19SnGQEgwPnJAjcycYqhQolKDtyB\n6CP4I6LzgSU30KDTX7McXrIM+ZQPPvd+xCtrk9XdSL3qKsIb2MXIINcHy6Mz\nuKuL2xVuX6q/Sy4zHoNwrZEWpprIt2W2JedfG/1/AKAwYtPUoHbXLn9DZNik\noOrXj+ZMCb7xBTGtEEnsP5xLBFc54SqzCeNfbiW4z49KksTuFya3AIWwdMP/\nIWU8QCHe/zBQzIt5n/NZXku6/v3b7g6HReNsM81ksrsXj5sFMKMqzzi/G0mt\nQKtr0mwh4YENfmHg//QdeE2DVZgC6GqqEfGZGCMl/hbuSnQ/2RBr/zRklq+e\nxtUn\r\n=nWAx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDdl0mVwpkHI867I3Rcb1Z79rdznzC8garYwBa7Mqa3UAIhAIzhV4KN3cE9PgwHkswX5QoFntOgoDHdh9HD+h7TWtyF"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kckaf5hv.0_1594631517663_0.48483189992582965"},"_hasShrinkwrap":false},"1.0.0-canary.kcras9pq.0":{"name":"plumier","version":"1.0.0-canary.kcras9pq.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kcras9pq.0+fdc190f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fdc190f87cf801025bd41495ef0ba37c018690ad","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kcras9pq.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-DwB0bT9bUM0tVFsZb/STZY9NfriQ9qHPs3EZtvEqQ7dEy2cYbA1UBEJMWE9edVgbhKOFSRh5Iog3N6PX90te4w==","shasum":"0a4b18769e098d36b021312c01e6b7f9dad5ce18","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kcras9pq.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfEp0cCRA9TVsSAnZWagAA6zoP/i/l64AnbjUoW8mGvQOa\nWOMRPIZqPbr7TfcIxntMjKkjU4GSh1GmFK9igSW6pk5W7ohKcidpJBHExkBK\nQCOuMD1jE5qyKUDQO8RDLiqS5u27ErMXP6drzldH7enQrh6v/aV3pS6rZdSF\nuHe9uaQSLUC1Zndq9EEwQfgpuI1VNKjtV8rvOn5qHd71jHfeURk2Id9jCX6k\nYzmFJ9Y0bzkOUkzpC9UX6Y5cyPfPQRqvWEcvY+s7Znt43mrbZL5T1v8yqQgt\n7F0MUqx/VuQNSR3TEC+1OXXrAzO5/RYaojIH133jwhOJ0tXmscjemK8+kQ3k\nFlvPBpWvHMtCgyPGGJnC3xUItlploz8Yt+JiFdGoPFFbGz5YbYJ92qbok1EL\n1EyvQaH3XIebyPmSGtwcI/Cmf0o36S8A9ehI6D9dC3Df3F89HFw4wQYMhrXB\nC3ClA5IJgGDWWvAO/Ql8bmTqGu5HX2EhvYU7p82mdz6uWC7a0kUS1xLf2iuu\naVihOtnjvCj08Raup4MAIJK+ID4dsFQ/5RTBkvJAfCtRyBwcOPoXG3ILPt/m\nCGSx1LlKryNkjqzMW88VCASN9X6k+NtcIxb8UOxJAUYKBOvCShteZs8Xjnoc\nqxciwSCeELwcrbr0EeLEBR6+KB4PndAZgkP9YEMsXS/jSjgFjizL4HwT0UoU\njZFa\r\n=QlrX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICUlvEkNLejOgga7Tz5aMzgZ1EYmKvul9eZH1NMHNhKxAiAC5vLXAAVCWZFmWjZQ8ZlTMMM0WnrA2lp9EiFoXFv8kg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kcras9pq.0_1595055388304_0.4412128898347125"},"_hasShrinkwrap":false},"1.0.0-canary.kcrinrbo.0":{"name":"plumier","version":"1.0.0-canary.kcrinrbo.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kcrinrbo.0+8cf3108","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8cf3108f2cbf5cd2a9ddf7da1fb02feacb1aec5b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kcrinrbo.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-j1EBjuJfbatSDv5jSNTQY2eNuJprNo9iR2FEpMj/ysJ8e2zqs4IvBJeg7nEVJedJD9AoepdmJdq4eQVgzUyGXA==","shasum":"f679b2adcc2e743fa6756571f9dbd1291b73c4a0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kcrinrbo.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfEtDICRA9TVsSAnZWagAAQAQQAJCG6A3iXyjcf9G7Ok96\noQQDWxbKJVwI5ru4AgStMaRWz+j2sgV6OUTEpYDH4aJ9hMwNjtwm96qEICi5\n3ndTVWVSA8LCipf7CwFRXo1jS0/y1NiR0W7e6c+IhxmYsvD5OiGA74q3oNQY\nDdG92WBg/CLHyYgT6EvSg7aQKELETsG/G23i0N889b2HvhEOSkegekHe1i86\nEa/A+ktgLrxsILjq4hl6HZpYScBzdtDDQksyvCN2hHcOzbgbYd+Qw7qgc7QV\nUa66nZJjDUb3z7PGOkw6lW9KwpL0nuknY8VmlDnAFvYhLiFgXnWRBCoKfmI2\nYsr6u6Obz33ExGmfKZj2H0iP6sTPhDioOTA753cHsrwy8eRolzPeJjjM7vMM\nfCh/m5CvytTgexkEmqdAsuVBJTNsHy/BafHhfKx84W3qGtP+SE6VRpJj/bKG\nYouZVlGwpaZpJ+S1T6En4MxwcdEwKAuVlNoREVNfYC78kQXkB3/q68IZu0Ul\n8hXUuRIChyLK8wVZjzodmTSMWrh3VfhLFXjPCWfDJXepS9wGtbqX7PrKkYQ2\nxUfutZMzJonMjKJ9Mava7uOHzgJLdzna5JH1Eo9o/SiyA74Oq4BHvJP7cdYQ\nQSKnyJ8nk9VtX53H6W1Q9YXM72NhXGUkurluylEgKVXfgnwsdZAlirOtJpZn\n8FER\r\n=AMm4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC60at/IOUlJPKn4xHWZCKd4qrEW6ojn02gf3PuVzk7PAIhAKUHrnDIVqqaqe/qzu2trU1Dn7hW20KCA4DrXl5+tjYK"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kcrinrbo.0_1595068615864_0.16875796203372562"},"_hasShrinkwrap":false},"1.0.0-canary.kcs5b8ny.0":{"name":"plumier","version":"1.0.0-canary.kcs5b8ny.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kcs5b8ny.0+5930d38","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5930d385ab838c3952a6a06fedda2417d803e49a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kcs5b8ny.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-LsRlWXC8eiARix/kJTnOuRCwM7kCYZCGbfUwDRdoMTyO8D9u/jBA4rGTR2GqJE/a1XMFhdnzJhIS8iA/m9bZ3g==","shasum":"4ef9672515f72de7cab9de070dcfb226e3358f46","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kcs5b8ny.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfE2VlCRA9TVsSAnZWagAAL3gP/iJ5fN2y737kimpKD4rb\n0Sv5rhtUZjeokolyxLGfEkbki32WJmBkJmvGO3ZaoGbvUhI8lnzcLFvcNZkh\nP4fH86rWtSYpu4AgcLlQdARtsF++l43aFRpyqIyMG3FDTHChKd80x+9sKhSY\nJJUmp2RfcZAGE+wkz+ti3/46Gu6cgaGTXCXFBR3RW6hPKS2O0NETc35uvFWb\nCh1EtmSkfTZkXsvIhgBgi1M1xvOReCTTkwIfTVHh60c951h+nKH4eSM6x3oI\n2KH7pTbzTuFEUkBCYXv51VRYOuV6OfZCAcsNRJkLirEhb+rGkDKGTWum8ErR\nvlxtHow3EcrMQZavbLhjZCeCW8hm7gr4xbPe5JUChnHcHI0Fo+HHYRoknMq+\ntT6ORNye08c+Bi/0nLSUcdDuDYDsMjRUyspUH+kQu81LwW2pDViipYDLTIbF\nFret3DTXyAlSzTyhh7RPm6WpxjAJt7RJJHPLTi7q6XSvM+fVhWYSjFqlN2Xf\nB6Rf9hxg8YRRyg6tWDBCkzA4HOC7H3MJHe/YaSBT695PqGIxo/zne4oW4eXR\nghU4pEGdY5oIzFqeLD+ROmmZAinZh0m1+2lYBQlOlaO1hlM6rRtbUomG1H8a\n3KNM2YkSMOlUzDVFvn+2MdAavAdSkD/LhxPA7KEjchHgW3nvNueiQlE9dqcU\nqz9Y\r\n=Xo4r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICMCm04WFxxnrYedHHNngqAOEqimzSKZVixoIlK1x3lyAiAd821SzzqosGylRBZAgNw0XaEdqkxSrNBclvABw/BWPw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kcs5b8ny.0_1595106660872_0.7428926889136072"},"_hasShrinkwrap":false},"1.0.0-canary.kcs8mlx5.0":{"name":"plumier","version":"1.0.0-canary.kcs8mlx5.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kcs8mlx5.0+d2ecdf6","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d2ecdf665d13b659b67fc5c360c7e68dc67ef5a7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kcs8mlx5.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-HYCMhD1HloMbtqe8I98vxjvrMQ9lMpPU++nXiT3r4dtamOJFG7eAWMyGuV0jNcj3IqVoPj+HucVzMsnojj4QPQ==","shasum":"bb068f958c21d241cdcacf06f8462fa50d755ddc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kcs8mlx5.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfE3smCRA9TVsSAnZWagAAakkP/jt5qNRvBfQehx8BttuQ\nDDCcHSoNgD7vzA57HbttS3GTs7i38GXs1xML31JnipVF81d8wV81OF8+S4fh\nHrbVyf6ifWM4pckg7wN0uXuLIWLbihadM9V51za5UUbohmk5WeGVAaKVICG3\nLDAG3IbFqdU9XGyk8su+zzoiqOdO/zWG+dLxQcX3L2LYhQeT7T2cmwNyJ7jx\nukHAaEQbEkVir6SMpr5A3TsHb9o2WliOJElZqXBWWhD9EsgF4XqTlt7oxniB\nrp9icXt9m+8RAeAETR2o00qQluz8d6kftq07Wgyyql3blbFwk/3R8WsIi0hq\n+lXUz+cV5XJbl4hiZMnI2PTexrt7CuaoCVkRVv4VKlv9LOasFy63GvXHVJyI\nIWp4K622BzYHtNn0pHOrRg2qcmNR/5lsxReHC+/YFoRjxYHoIK9hEyv7vLvq\nMrmSrdrZ/p/F+c1t6t/Pq0wXlEey7BZIKrllcI2g7lijW1yPChCPVds5ZdSL\n9I3YXkchmKX++3iJPK8MxqX8VW7iM62mtEIIhHJMWCaQ/2FYbCDxVXyHkywe\nSykm7fJLKINHAdQZo94ULy9cT4BYcgW53a84/GXjvqOmXCCeuvi9vxyK+DSn\noDB9LTVcix6OcUu1qeCBa/sLlbAIpqd3r5jOj83HUf6jjtZkcYcZ+2/OPo5N\n9+j9\r\n=giDF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAzvvNd9XiIwICITJtQluUkWCtV0U3rQ0Xm6SgFPvuV3AiAVT74bxLRJiUoDSn+57iJa2kZTFiDLeqYo3muMxc5MKQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kcs8mlx5.0_1595112230341_0.20284084133342062"},"_hasShrinkwrap":false},"1.0.0-canary.kctvam2m.0":{"name":"plumier","version":"1.0.0-canary.kctvam2m.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kctvam2m.0+d32dc04","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d32dc0490fd4ced6378d0981ee5395cbd741a174","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kctvam2m.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-5hZG34bBkZjEJR8RQKFY1QtTv7wcOa+F5r7pSDk+a/F7t3SgQf0sC0e1KySD0bsk1dJMi0ePKosOl9F7FFlt1A==","shasum":"557145bec478fdf34f1ea78f029350bb28f9b6e9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kctvam2m.0.tgz","fileCount":13,"unpackedSize":31297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfFPwRCRA9TVsSAnZWagAAQmYP/jaX2WqT5ABYjI5ZZ+wT\nuLq+3rkk0vrLKsQKw2wbs7kVgK52E6CCpTs9vz1rS0MZAFnnT2C7D+k3VKsI\n3ZxJN+R3mO8TkjGOCvqpmyafjSBzk/399wJjWXj6uCCuawbcgYmGsaSqRQfX\nFRv4WftHpNR9hOW7qMkksEQ97ZB65Rgvf/FKz+8URYf1F9zGDiis33/SKYRa\ngDmnWOw1AJVm7Ye9/70BAyTeHSmuBI6ebW+PhY2oE4W1+f1hFCtL8c6MCdTw\nSzo0WROZq9Eb6KDLmdG1+c40ypk0NtYBb8JcOoOCHQwoVuFJMO2dbRiciHok\nsHluPDaAJFlbx6YlXguUfr75cSb/XVTpRiVW7crOi5cXvFetpUV+E2JhVlgx\ntUD4EKohXWURhH8JQYn/8Iy8vr595svSlitTe9GVDcHSTSQ7TniTB3UWI0aP\nTqtTcNVVBjnCDWLV80+SbyCee4+ubPWquXU/BUl4c2aEx/7T4wc4Rw8gLMts\nes9pgD9Y9bd0LfVbJv8GyE7mIcO08PnaNttCddil7Dazpkc933khQ54VS3BT\nv439+BC7rK4V/5Lam3HLmoBdEiJhNICYAnO+w+0ppTx4TqYXC2XCrBKrtmZj\nTLo9IFRkPkMqaM1720HhAVmsAsPoDLTvvxFDl9Ud6smFdZJ5IS4rjYZ/5aeC\nNwna\r\n=BbGA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE2xypuLxlu2/O1kLO2l004OFwZwoVLi4YUVKUAEaliwAiEAko157Qrje1rfmAx09luU8YN5pDZxIw2vh9pSE+355DM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kctvam2m.0_1595210768674_0.2676362846582605"},"_hasShrinkwrap":false},"1.0.0-canary.kcvksj6x.0":{"name":"plumier","version":"1.0.0-canary.kcvksj6x.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kcvksj6x.0+e5684ab","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e5684abbe1be7ebcfd212bdbb483341ed3036ca5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kcvksj6x.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-pT4rROaBrm30QuGOAlXcrJIg9Su1w+lkzAhbVWd9+k2IPnGtM6V9MGoUrCAEdguQSU1sNf8fQJmPb3HYg313nQ==","shasum":"6063746f9f07ec25b083ac78979cc4a127fa3b52","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kcvksj6x.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfFo+QCRA9TVsSAnZWagAAKcoP/2+37tT9/fwojzI9LCSD\n6ukRpVG0ckvzmMfVCHehPNlyGfetGSyeVr0egDu3YLZXD75BPHBz1fny+zaL\n0nAqD+jhMSjtz9hOdIYzxVFqRQFHF9vuUH6hUHGp8jd6coMoxZTdkr68tPXB\nMuA4EWiIJy4536L3F7eJRDn5T4rMmu5rKnUvjCdRoLwyYK2pB09VjmYmG14W\n7xjZ+XdT7wtwA/9EGVv3e1LTSclRdE2o9f+yCX2ThbrFIXzz1ldxLRjeVlTv\njWRXKE7AiB3bMtmnARPLPHAa/Xt7spUDBRZD8SivaaVVHFMH+Pig97bdav9k\nQjPrtR0kwhpTzFiORUichbVAhOcSASnmeTMcVNRYJPoJmjfczN2dFQssZ/qD\n3wukS8r0bng1alDGWBUorMAebv806JKo5/E2RqhI0FEia8i0u+/wSNvXV0M8\nTCFm/he4Cm27+np1YZiBmBr33xV+xaJjiohILcToU4ar4mIHUWONOZyV6nfp\nqBHokW9wS2gQPEPXj3m1pwW1u5Qq0IPrRSIQi7rqn/FNRBI6REosYCBod7qL\nnWDvw87+xdpgNz1tUYtBZocuJ4VjjDXA3w+gW+VibKJ6/Skt6/gOxABhHgle\nBr+8tCTrGYpYwncc3sgRNP8UrfezCRvjCl8/TtJFicF++S2MwweDZXl/PYOE\n90MI\r\n=Aa3q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCHVG5k+DHgR4iDxcUj7Q85NOq2duC3dcrxv0MixNUKpQIhALxx/W2Hu+oGwH8raA3kP0ST5bn9z1nKx7cG13vsITTo"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kcvksj6x.0_1595314063774_0.22497992777324405"},"_hasShrinkwrap":false},"1.0.0-canary.kcwgd771.0":{"name":"plumier","version":"1.0.0-canary.kcwgd771.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kcwgd771.0+a07cf1f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a07cf1f3a27f19abe304ff482210ae9b31d4a222","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kcwgd771.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-J+Ncl8lmx65u8xWbZ7XO5xvofCOSbvolQDNNgIC8Mnf81A8WIEWhFAOJWClKvBsYVOR1lyEfSUae0wlqNNo/eQ==","shasum":"3b091e1a44f35bae9b253c192d808ba2da875da4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kcwgd771.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfF162CRA9TVsSAnZWagAAgYEP/RMcLHIk+pLeHyFOfYF7\nGRx8BMUxmLnfJXePtRBLRORtjlhJXXg43MyPwVK4ucwlKOuRUeHvE3iLJtIU\nqr9HHW5Wgp5/JNKeWKfOsHqJhEQS9QfwaDtDllnBGD+2NuKDBW5uneBnjaiT\nsfR4sqg3u7nvgj5iUXjxKe792uGD6u02Q4pn8/vzAbDmT1hSMcWhSdNCOrw7\nU4psWIGuVeGYvR0AeRzECtAHeKOCYKHZUAICYRbIuem2xZ37LCJDMjjX5K5g\nSFMBCjY7iXbOyUseYmNOJ1C2sFuRSxQUFl2wjdi0BNB1Jr4t/Hg/oyA9ZpU7\nnX3Abf4yhWS7266cbEVZISWdxxmmlYyeNVTU/NnZ0eCAgq18chM0QlYpSmLE\nXJf39vhYIkAiYv40bbPUxCdtDC4JQJ4KXQYcCAAanQru9adIoOwUCDRC4be4\nd3AGXCCj0TL5Rvwa4OXtpY0xwKrJ6jmhCAyH9Pb9r46AQMz/H9XK0FNyzWXT\nzFXDc5uUq4RB0qdtBl09qh8TV8ddZ6Mo4P79fosESFVj+aoAMG6+dSfvOeiN\nWrV7PzvB9rlkvqqY/l2AMyyg17rVgRSMpPmdhc0ej5bkCj3F60mgSEaNMb0K\nbdqkKV2C7FwPWZfOmxTG5/d/xkxW/RbiAC0W2Ng6FXYm51yv4+Od+VlMcvkI\n0caR\r\n=ZRzv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJl4CoAuEqBBgiqqGRaJF6YlmG5i1BTrbkp/eU+7P57AIgT6EORZhD49gk375JXLuRE3NCWhXBPcKyn1kXkHQRyLI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kcwgd771.0_1595367094475_0.3457395809812187"},"_hasShrinkwrap":false},"1.0.0-canary.kd1392pj.0":{"name":"plumier","version":"1.0.0-canary.kd1392pj.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kd1392pj.0+3b04621","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3b0462189ced23324018c1d01723940299a6c77c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kd1392pj.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-IGkX8qQeobELk5RKdq6vaZOUe1r5sWzVWv6TVJGqpUmVq/BGwxsTFzjuk2xrlv119idiXtu2Uljg42ZWhm/Axw==","shasum":"7f7e56683391c2df52f650f69eda4f8a12e8616f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kd1392pj.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfG6WoCRA9TVsSAnZWagAAQicP/iUmTsQEXa+yeW9CM1AK\n6fjW15zCE1a71J5BcK7/v1jxnU0nTrF21nHQVq15/lpG+leRDAqpey1PExLx\nXb4Hk/0EikhSs41i5POt/WonkwfzrbmAmLuGYGacGSv0GrGcoGXKuvSTd+2O\nnHo4v4wc7LX9FyXq/LvtxxEFMVh5I+77fVGmgW7HmSUsLMOqN8dsURx4yYS1\n39pVdpDKvpWJCooJ4OrdN5MU85mJ4c1ev5tJ8zrcPeqhIvbONvTfMyZGvLvD\nva1vxCMWwV/qxJc9AZaSPIHFw0Fas4+zksSLI5RIOu4gXO6AAi2svW1gVHEh\nV6PPh3mRqEe2byajnrFnYeVDzV+MI5rBZwss4zJt5be80mJ5YakeOGqTqLXE\nIaQRPKiyuU/6zvLIIKepbSUS7AJuyOzc4C6509/vEFJeaE5iTOATtH03XCt0\nEkrfDcWDoIIHQbucIMSe2pY31xKz8lYsHOPlXpkqdHROYGBls8MQM03Y2ynH\nAxXRVUPRtzBaW5HYCIP+rrHc3xVWOVP0AA1r1tbJNGwBQmRtj2FPqMdZRMPX\nrihEPrMfHxvi/Rgkyx8q+vRxR/53CkedcrAyCMQw7/MUPC3kqLcGwSg3bDoX\nt9RkkTPb2P5yjqu5VHJQVMc4rlYBO+DtltayszAXMsZuURZ18wkekb17H+YS\nUmy4\r\n=XGoM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGrLtml7fcb7/U3Q3RGHMhT0yxbsRH79rVV85ScNt760AiEA3xGv5apBOYbcGXlwfX/WSgKQN6nxSOjJfuqKfAFtAbI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kd1392pj.0_1595647400289_0.6573904507137773"},"_hasShrinkwrap":false},"1.0.0-canary.kd13oe59.0":{"name":"plumier","version":"1.0.0-canary.kd13oe59.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kd13oe59.0+25feb79","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"25feb797777dbf094590690812eb01976354fefa","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kd13oe59.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-4/RdttYCoAYU3FdqovCPSMRX860yqs8GkI5bG4OkfRCrnoOM8IQjMXvfux5EFr5HqwdhizXWBBSKjSrxkxaPoA==","shasum":"64e9735a477125e5c1bea392aa50a02e09c62691","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kd13oe59.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfG6hvCRA9TVsSAnZWagAAYmIP/04q6C5tSgt/xvK/Nmg4\nmxJJ8BHSCoCEd2ah28MBUUlvEW4pGZSov15lNMWNP7KsMq+R5CeTxSgQxwd+\nJ44ZDxeJdyRv2z/czvGMKfLR+dFoKla7HnmemzJbL8OqkeoHAYH5bDDMXxCf\n4bQ5xZJGCJiBzRnw2D+YLdBlvAIGvzpMXqU3EcC4QhJo801UapHb5SJkGUvr\nXiCxDlG0PaEdJs5QKNLCv4MwdFBXD3ArA3PFYk1hROMYwtHPUVD39qyGdYiJ\nR5+YR56xcfoM/6Y1YWjxVJTlNUmXT26QMuLc81qJ0ubn8sNkjYrx925RMYEN\nrW4ve+x8I5XxjoQ2vrCVaIcm0TvRzF2kl3KNPGWfxgU7GgXA3AOH4Y76S91E\n1/ipknAWzrPWc2BWHOZvqG2yjyIO0R0S4mj5qWEsees4o1/Liu3rZNdr0tU1\nr1mkemNDKMtreP4jKO5nojBzmusNa7R9+PEXQ6sKKsQEKxN4AkSU6xpCunPe\nAEhHO5/Y2raxCswrBXArueVhZyDRnrRafAruBf+71qLo8Xu25qrMMZJDa5I2\nTx4C9GDpEQhoUJE4iBAJr9bY/X+W9Hyv+4G/FFigWtm33+rCzmtVAzJm/TE/\nw9Nb2GV9IRojCkAQ/1JJAgGTJeMAKGNItBJs8Sb1U1pv0PxuWqzeHu6xUspr\n2086\r\n=5Au8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoXRtV5KkoOs9mUWsjzv2oLe1nXLUNtwXFmYGZQ5iS+QIgRReJ7J5MVMK1m8+WFpwdMUarMBuYT8LxKuJ8HPOewmM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kd13oe59.0_1595648111001_0.03204084656779682"},"_hasShrinkwrap":false},"1.0.0-canary.kd18a18n.0":{"name":"plumier","version":"1.0.0-canary.kd18a18n.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kd18a18n.0+10ae92f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"10ae92fec6d9aca11ad78a96578b7473c8a50af7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kd18a18n.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-Pfh6AS/nHj+G/J8C6gxTBOFO9uJQyLqBqWXrLGJ/Q1tw6L8X4VMNNgL/HHEyO+D2w15EfrY1gE0/UfS+0ptq3g==","shasum":"f939540f349867f38664abe8456929ba5695b06e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kd18a18n.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfG8aiCRA9TVsSAnZWagAAG1cP/ibQTDB0sIBdfiT0fbua\nbBLbGYbdXu80yqEIQhU7wDpjZ6uin16+PuqQ/TOlSav+uEDTgH6alojMQNb0\ngSEyUv9J4fYMJputIcua4Rwe0gAiOUzDNi/8Ns0fnAUMTd7lNKGRnFRKgHXK\nxhcVdC5F5WKxABa9Ag+GNoNEmOiT/7CcEGZpxucpUnd7A/6RtK7glTIqeFKC\ni8fD7TgDqlN/1wqwZ9yVSEv5zs9vNZYsak/tVTz913fxP1xfce/AS6hQ+FHk\nvDY1hSCQ7ykW8w7QUI4L03qGXfRCpK8MjRIugpvHU3QgMUPobjCxa+O6HbaA\nfvfMbfSndf9SfQRBcMqak+HsvJGcWDRHPq493G3Y5NIxolZ4yAaBEy7Tcn/I\n/aYrsPtJJ9vKZ6TrPnEFVwFjVscXkpwmNsPgAl1T0JfIpZuaYb7NV/aO8gRI\ncwpHLvoKKGdtOWMqP+eEd4UHaFiqRBkvelM9DmbwMwSA7eawUqMR0grHsAgO\n+Fy0N04CZMJi47wbi9rkVeTdPGC5U2dmUpsrmJow9KuT5n2k1YH3lI4yhn/f\nz4h7BbsZcZcUak34Zd8sI4mp0ZsiVAeRvmw/or436K4nwo9C67N6s9UQQiL7\nRfpZ2KNLiWO2ojvlVMr/XTOOhQ5VLuhCb3XhfcmovXFNdz+XcxAlxKSvw8Qr\nQssy\r\n=XKYU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHrUj+hfUvIG6F+a5GosLLMF8k6d78OBB4NrwzujU/3wIgFGRhfIiPvCqfeYcSoh8ZsSjw7t4nrexOwiDBPamAV/A="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kd18a18n.0_1595655842252_0.34535146206622547"},"_hasShrinkwrap":false},"1.0.0-canary.kd1i13g6.0":{"name":"plumier","version":"1.0.0-canary.kd1i13g6.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kd1i13g6.0+9140106","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9140106b1b8e5b6a1c586c6fe7280cfabbcf1d7e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kd1i13g6.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-iRpoEs+/T8FrmXE0MFnaZfVIpOTtsJnyO9vfUZEa5lqq6xrygyElgNXbeObZJH85MDlqwZaltwoeSwSGd9LrVw==","shasum":"0300f99a7f083fa7191f9b35a136b9b9be454514","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kd1i13g6.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfHAadCRA9TVsSAnZWagAAg9AQAKL6rosNywxdq2c2qyYK\ntLdZwWOBUnYq2K/9Za8HOqaVFAb4Twb+Ftoh6pqTD0ue/I1mWufGG37JKtFl\ncis/lqxkZ3Y0You/DagWEq8E1Zip0TQRodWeeHc2qRT11oT9sgdcZb9XV075\nszHQlHdMHQeeTBAMY3FwjwSquvgsspyTxXwY6om7eppsSZ5djatuiViMZtj3\nJxW35nbFFCeZ9wR4hJLzNTMCgUejwhuv8jp2l0L+bbaSJLEfg/2IhvZTR3mq\nYsqXBG2CXC+Cg5cu93Y2hqDw6W693eZyPKH04q+KmKWdB4rx2Ch3Z3Rr4AvD\nsEUd7EWBIGbds8S4KlVx2ZUJ7kAX0cvGlu6xF9N++qvu5pev8Byo5Ub+8YmH\n+Wwq7bRPU7ltT+D9zTKdXSWYKMU4P7gT+EdVGKsMLa1T5cpK6CHJzIbjCRKj\nJ5tcjzG7TPvZE6r+pbDJSpPs7w2TEkT/j4gg1DXQKKgsOD1fTeTHLcXvWRhH\neqj0lpu3Wm0xUQpPWZ0dBMTaYUsKhy3woV+fVl1K2mBVtVXjXINaCe3EB9jS\nacdvkduw/x/23+zMzYr5mPhG32f3JsOyEPhxeu8u26x8TorNk145oqX2Edt4\nAgwrnSx647fhzj6bqR1Z03gZtH8JOiT5x77+pbh45smwAmUfv6F4MZ4aj9uS\n6lsH\r\n=ayU3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHx2vVD/SoLXUsn1A0HgU2gL1z2d/bc7JWEWDYWBtFH8AiBNUAyAw7IPnDDUMKYIvBGFvRp+VrMt23vfcUCwN+Sa7w=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kd1i13g6.0_1595672220957_0.16946527364923836"},"_hasShrinkwrap":false},"1.0.0-canary.kd2p0nhi.0":{"name":"plumier","version":"1.0.0-canary.kd2p0nhi.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kd2p0nhi.0+d2b386d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d2b386d786d56818c61c31939a53f24de7d33906","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kd2p0nhi.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-aS7COSVuni2bO/7CSxe8Dt/ycWkEu4BIzU1i1/9ezM32955ltdh/PmINoZZMoswGTwiHJLqIJ5/f/vyW+/+iPQ==","shasum":"4a4a82ce545e19d2c9ad62311b152e5323cbb708","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kd2p0nhi.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfHSCoCRA9TVsSAnZWagAAK3oP/16Z1WK9dv33lzEMJmCJ\nlY+EP5OMfWhuJIuBozwjSisVaFTE7j95mQI/SjKIlick9axmLfrfONxZXNf0\nkwrBSTZv2gvanwE6HNzXU0Gwh9W+h7HArSKTHtbEehXHtPX4Sz644NK/+w2c\n+Dfrn0MhmQBcj/BPLC8/DnPYuyGD7KE7TnLo6tirM+yoO+CJBerIThaRHrkj\ncOzWJgI+i/OKlmADXDx4YGD+y1oqzjAuJLC3R5WhKLqRuxl3XnbjyMJ+w4dZ\n2VxmH9EeaHvfqIQTqm6fK01o0LHp+Ww8J0ZslnkKCrOxTpG7z6JdzgfofOLD\nlAHUkp95/ah/jiUj6jZsNqdpTvLpL2Oz+PNSFg0sRTk/XJkHkVEniZKPsUCY\nxnTUsYvR2aqKfIK1CDfuKqhduugIEirmo1wHp/YKkaDzcrd6i0Qo6Mn5WqA1\nBqx+Jp2vTloEus/F6icANl9B/dvgJOJmNVY5opsmQQZ36CVMvV/sJlkf/INS\n+DXrzA5KQWw+HnXtlkq9ESzCSfwuJJdSt9ZW3FSCikdgviHoW7tHdErBWvOS\n5DSbGmrP6jbp/bmDHS3EuqY6UV0FGTpFSe6NoY1aJ7BXukos2ZWmGUjGkHLZ\nbs1JTU7/P3Wsb9tSPZ+gyMtiTculFMOHmI6sk5UmhibH2SlM6IVYsdRTNR7H\ngNIM\r\n=cu9F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCYHspYhupaYfEh2NLaHsX2IkPbjr9bFYse7orXyAH75gIhAPAc6bjZUkQJOvre8VRcglgwLJmoJlK4UUgt01JL7Dp7"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kd2p0nhi.0_1595744423506_0.17966547214013873"},"_hasShrinkwrap":false},"1.0.0-canary.kd3mpv1u.0":{"name":"plumier","version":"1.0.0-canary.kd3mpv1u.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kd3mpv1u.0+24bb431","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"24bb431acdcec517c3dafb55812e9b6014134ed8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kd3mpv1u.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-cTBLDRn1V4xF1+Mk2xKKaboexXEZto1tfErLsWmJizzeY6gpFlqL+EhijeQ22R2GRQPSJUu3LmSvvf6UK4KSKA==","shasum":"a66bd9bad9370b36a55db66f9eafaf7b17b8d946","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kd3mpv1u.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfHf3BCRA9TVsSAnZWagAAPMsP/2JtArMXIIQAUHDmG7kf\nkJr+4n56GQJuhiLJxZLkCY5TmJQ7vpe2AMgeNWDDZBUJfeafm4CkWiOG6+EO\ngJqQ+94Doq1hVayiULxPrI4uokJm02+8+nVdtMEwbgW0ZE+A+ChZeira0oKr\n0Lt/kD8XH0XfOlBcgdoz4hrhnN6fsXUt5/qsnhIImSVUvS9OQPqi/azd68jb\nMCO/YKuyU3Un7FsNKh1Rn2gGxUQDXJcZLRzRU+rapvlJCPfSNQHox+8rryrq\nFo55s75N5/GCPyWXP9K9VVmur9T+GFJFSxn3vGlMeL6Cg7kq+6514tsBdktu\n8yycMzDS1EkCd4qETp3qaxdMuRXMKz/GNV7DA1Bq1m+CBONORU1Z9HbEOZVA\ndZRTwOuHFG6yhTnVGQszLWO0SFLbMnDY3Z+7p1VRTPlR32wnB2Sop19MHr2W\n5Zsh7GfYTXf+Mk4BJUmd2UzjMTtaLUPMMBSFY5N0EZPVwG1oNgrb//AUiOKG\nWfMbv4CrbnGY05mOIKIpEdVw3u6AKAzEFkHv5UuNKCsaqeetadUEW4emlgrF\nXFchbtmKeuCeUoYu+kIqR5Nbc1l1VNIDQcFqYgzr/iFuOPxzM0gw8p6ZgSnN\nEsE0SYRmjXAm4yM75gRgLvHk2dE++xz1pUy5FIBGkgFQGnxXjdT9C7mjwbLf\nxmg0\r\n=NVIw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCWDK+vfZ2S6Z2ppORQ20EIV6eORwMFgYDmMDyUmH5uTQIhAKIYMg6jtgi+HD4CynRNA11B4KAA5QuCr0M8Wm3GxjyH"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kd3mpv1u.0_1595801024858_0.6430142828534846"},"_hasShrinkwrap":false},"1.0.0-canary.kd3rqvd4.0":{"name":"plumier","version":"1.0.0-canary.kd3rqvd4.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kd3rqvd4.0+ae9b8e1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ae9b8e103ae6be253ab3cebf401a0e34689da1c8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kd3rqvd4.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-K0fqfPBJIusmeXo9IM6lv4Li0Ek952SjAxoTy6ged5B0QPXg4LpaX68ViCcV+QfRQlSBKXRHMiy8790XeQNj2Q==","shasum":"f76fa136f36799e1c2ae62d7a0c3fbc606a78f5d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kd3rqvd4.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfHh6/CRA9TVsSAnZWagAAjVoP/3Ozg5j0xgjWXzA0lXAg\n89DCP6ffkqMWh4Kjv0YQY0vPzhxCpiysJmetgLPJDeZk306QcQbFTsqb1dKn\nOQALtCdYC3HxlxF4UFBjUBjEie12+niNLGzVL+L7Ma5ADr15czPxJZeBUg9q\nDizthKfSP29QRCaiEDKGVfJWzGKRSeOw65K8+kYtSg44bjQ+FCdLn8cBcFFQ\ncCEOlhhfDVoeWxWSfTDWIGPELV86quR4Qk2EkGmynx/efzvR3LnJrT0HK/OR\ndPCJlMZFmEFHKQo+dIqY7e1juTFzccEPlr5wWIbNU0lF19EK0MrIppcCUfrK\nJDvVAhuiTqw0FQTwkRIYNYvKBBzYiYqdOmav5EDUwwR2xuOYJpr0PqrUoeT7\n++a7SZwyVw1jeicqVNlRwEv9TUgyzbhrT+XoxVtPYeKmSW06kulIiEm8Fdcn\nSJH0EvlxeTNBtxNljr6IxrrBeMyXT/9yi+pMiepwVeob7ACDvLqnqvlHUPju\nK6gckZYngzbckrerrJNrRJA7SHt+BxhP3G6JqOMRh2nuI/WWjdhzZOgYBc71\npxU58lca65GcT2OLfj1U1nthZZ1Uq+8larpQd+0umpcntE0DeoEab9nnjPqa\nNIn66wsUxy+8NPPUOjN0UK5qdNzsBsGmG+doNbLGkPTpMfoWqs4JBBXrfiMI\nRmFC\r\n=mBtI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDC65SqelWSUBe9H5FW4d1yOuxoiCNe8VLJwSHdtBfXFgIgX8yX9DzZkan2FAZDgjtp4rsF7Q49WQ2CkOo/WaQWaH8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kd3rqvd4.0_1595809471208_0.16623291969340048"},"_hasShrinkwrap":false},"1.0.0-canary.kd6wewq6.0":{"name":"plumier","version":"1.0.0-canary.kd6wewq6.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kd6wewq6.0+3251487","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3251487851196165e3ab62144161d3329811b4de","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kd6wewq6.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-IQ9FOGFpVDJXis/JksxW9dtOaplaONOIlznpSrNOCIf2rAZWqy4PpRYCbn5VocHwATyPTxMQRMCsETjVGp0P7g==","shasum":"3ef72d97e11ae8551292cbed7304f43e42224174","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kd6wewq6.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfIQH1CRA9TVsSAnZWagAAYYwQAIjuxrpiO/Z0hMEtMdjd\nz2aY7DaqnS9kbh7THjGGzj0lPQ+YF6M7fgMxttF8OknGXeXwjlpk8HJhKpch\n2KX31U2nT4j8WVCk8JdVA6jMpqCs3Y0KlZf7BO7bN1KTJ0UEfqT/370kbYng\n3AJXf0yoPNSoTnzELiIh6uUYY/ZWbKu0qTJTZjxrW+e5PFzknREsoIgbvtUs\nx+Wmp6kvnPdbBY9F2rTyCCANKnq7blRProzcbBQSn9w23LAO0zCIwz4OVnep\n3rNfm5SxZKeaXFJwmBzBTFij4tK8u8eAP6MaGPL18IQC5iPW6y5THWuA1OMx\nkSzgpxeOdqg7Eff78QgsC5CBz1zTryD46DZru6capW1gNE4zl870NfuVTneG\noEj477hygfly3gMJZLtmzVK+4jlLbl32B2hfvc8fw7Qy0FHH5Xvymy0iaHe5\nKq7T/QuNAXrLy6S8aQuYumtdyIlhwVPN+10cip6CqP6MnjDEaB57311capj2\n86W4rN9GMeS/1/5DgM3JHRXwQ96TuDMnT8lbUAJjaWJBr4o+1luRbJWmnlig\nHExK1oD21A53UkzNyi3Iwcm1NGO4WSuo7aGBe0/RMBhXkVCWgIa98EetUNkU\n6e06hDiWAuDCxp9CzkyUWv3MiBaHHRg8vbW9NBBxRJDYfha0rST++XqDNIby\nYe3p\r\n=93ux\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID9hUoXS+7A4qp4oah4oDHOIL1aWvNCnA9xnNFMmdDDAAiEAupP+1JhE1yEwgUMRs9L9+vi3miYoJ3RLV8vySAldqwA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kd6wewq6.0_1595998709107_0.10649590196553804"},"_hasShrinkwrap":false},"1.0.0-canary.kdf0lwtn.0":{"name":"plumier","version":"1.0.0-canary.kdf0lwtn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdf0lwtn.0+b159de7","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b159de7becd3e5b54dd97b796be4cd7715f9d3e7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdf0lwtn.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-3Rkk0vn6/BlJGWsbAuima0QFLWK2CKp2VsGu1lnSR+qwLtEvXF6khg2HjuqLyqMa2OIdVrFe4MAB4RHe7GCdmw==","shasum":"67ec3a9e80e4480f24ff41f1680dcab5d831ab00","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdf0lwtn.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfKH8LCRA9TVsSAnZWagAAxFsQAIzwQy7aA5hAzofu7PV6\nJiEiQTKExjOk4Ux939Z9a64Dz7Om+DHgFC6D5w9mPrRZwqGww9A9v0stiqXS\nXxnHpYL2lqS8WYxUrbATxGplxg177qDBmVPLuAvwouTdbR23tSEMJ5nu52y1\neSTPXJfchWLjlkRd7FeyO4tTKGjyPjqXZF5bs/DU0wtWRzNDikNQVWY94ORy\nfBRR70t9HH6PZa5JZYrjcTVLLUziud32KTUkmbV31gVUmfoRLLcx8LDfTA3y\nSsGhsjX66v6cGFC7khq+ny4CeN44XcxG5mrRmB7tCpwzVAWVAG0INAisMYKr\nwet80urfL/5ydHQZZixPtYR/Kt6rvzQy5uVM5Ma+lWOyV3lad/IqBVZ1AsI7\naQoWneD+JgIdLgyzEPUUZDxDK3tgG6dpOfrSeSgYxlXMh/9Va8yf6aHVmquD\netema7tQFRA3aaT2SvNYPXCf5qirhWiOmeuuuARYY+UyAvzIlOywi4/waQYt\nRSRnLMEyuTl0zCBVkWcDuyXdDMmchbt0S7CiYDacm6voV2UqEw8MFrbnbX90\nZVlnbc5XFFdTz2vt6wFoymx5ouM+xwUJlgZn/26PBEQaxMeJP5bV6TC4AmQ1\njtnKK4F9qhafzVpNSwbSJ5QnoxXaw2wzIt52+vUOtsvt66UGGnvC2nIruU3j\nKNAQ\r\n=QQn0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCtic9ZghVqNWKYEvlyZe+4cL47vCiM8njrPo/TkXWM6QIhAIG2ZkLM3JJXZW2hrUxGCFRp8zYsDqOPPM91EnX5u9lw"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdf0lwtn.0_1596489483594_0.9586569306470303"},"_hasShrinkwrap":false},"1.0.0-canary.kdgfdc3a.0":{"name":"plumier","version":"1.0.0-canary.kdgfdc3a.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdgfdc3a.0+6bc1254","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6bc1254bce8dfd78d1803f98e24ae995540c4907","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdgfdc3a.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-aTEQVubVadLmt8pRFN7Ky2OPvRy7BHV+F3sF9u8QOl0Ovs6vy712vn5TooUVi1/zWo/tgvI/RqXGWvCicwt31Q==","shasum":"651f07a3a9b5dda66a2a33c49c0719843a8fe02e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdgfdc3a.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfKcwdCRA9TVsSAnZWagAACPEP/2JNFwm5JZVfvlk+7x8B\noNxUvc+gYHHWspcWm7T1u2/u6aAH4MXkTJ7HUt7IthD3zeAepbzrlK7bLSY/\nv1LxMTYELTMt63wv4AJGPVosxmjindP1ArXB66ynSGkcha7htaTaD5j/Q2Nj\nGk+XGU+FB3LP68Tjrn3/tLbqL5lzKtR2cQxMRxj1LJ66Krce7Z7kJbeEb36X\n8v0ZQnPhzSzMqGWlkU1SQ7NkTq1uNv4uUiMfORO9aBf6bXFr1eRXOg7ZxT0k\n9w3nXIOwlkBNCgidf1HeVJEdarRAWSbtRBOMcbvYZwy5y27vFfDD2smjYGqs\nPMr2yWbxy++s41qHyScWo/XtVdx9+GH5uD9YkRylyKPc5Kc1uoxjVb1p2/Vw\nt6I5qV6EzL6nHJv0dylKZpcFhqJDT2h0yN0XYIzXDrn9rzeEgEqVGaWyZxsv\nyr/3Nmwl+X5we406X0md6GWfjihM+crZXl0EPrsoPv8V8sECqrSYh9V3zf3v\n6TPTDM/P7UnDS22E/Pc3AXIdiUV/pxV3YKvlyTn7rZ+25AtWxjQC1Nuq+mGq\nJ0lt5LtclWRVr3kkRN9NTzZbeDd9x7RChJeURbNfzFhk2zi4u0Vi/zIo8xZs\n7jYEBodGdxYBx3HDV22qxnGeQbcFIOkKn1TgKD7AgD96vToaOtfZ+jIlK7gq\njjpG\r\n=L3Om\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCitL4vrBmq/Z2EA9AJ2whZTBJmelQd4oqLohTMsQ1lygIhAKr8DS+OwJ9rDd2SLLEUx6T/HnLK7tVdLFwCw0/eOHG6"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdgfdc3a.0_1596574748416_0.8749418430510447"},"_hasShrinkwrap":false},"1.0.0-canary.kdi5mwkd.0":{"name":"plumier","version":"1.0.0-canary.kdi5mwkd.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdi5mwkd.0+059fb90","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"059fb90cf50f0674e8bba366fe055f0e8294e477","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdi5mwkd.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-dw8FzEEHPZbKBUwRNi/E0gfWS2hM8a5xQe6Vh+v5fEjJZed51Jx5qooYqQh2xwUmZiEcoJkCm6U/gNd+aPP0RA==","shasum":"2ea3289505b4691b367dc5755112fa8d9c0ed4b4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdi5mwkd.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfK2SlCRA9TVsSAnZWagAA5E0P/ipy4pPsI61MTTvnmXl+\nFSIlfb/vgj+LBzis2P4sZExxPhX3q7ZMiT0eOodbeKluw7j1yTvihXXk0ow7\no7KFRq8A7z3YHWpGqLz9IXEcjVhMs6UISCxPpppCeaXzMBR2gc8MXzcfXkLZ\nHDf0VtzJcva2s2MRHvpjUfkbZISnnLrkrzNA2PNX3+GgPxb0/oQGwjFC7zOl\n3d/MIuEfSAdFstzMHeFhRW3B3W4sBVjCEZzYv7BOd+Q/sBfMK52nsI5FFXh2\njo+Jh/cRJ/o7eiVQdZtV5eRcKfmjM3mORC8pMJ6Sd+JxZ7ejAM6E4bsytFEo\nkeax+PiQC/W2REscwTTdm4+3hksm1vz9XQsQaDqrl+QptphGlSTaQRlx6btw\nlVWAwbsd6gj8U8Ct6Ngpsr0HXq7nHjP/8cpWmceXeA6Zk0uhsePk6ImEAbCM\nMApB6s/nFvPWDGBTbKomzBdCduGH3ih/uQLhAifovpJ3qbIpbWnhqwiQ6r27\nw3kj7m31sjbqvLG1WWy+7XtUJg6k0KgonUklx2lyMqutq7x9lQYvZ2bwjoAe\niajvGCzwY1KeIXNL9FEwvzcKtXcngyRa7kvh09hTweRpMAUM1AL/ajhbBnSa\nTyLD5F5T0LZ1qQGVTwJrXYqRM5iVIfHe9H3sfHyo5dSkGt0jDHA5GFyz3geH\nKAEB\r\n=ZJ0x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCXDj13sjoZO8gZXEWU6WWpj/KLRgcMdQcSRl1Y8rjeKwIgdCTaVtkHZ2tPa6kDQhjT8XpmszNeJs6WHUIGQh/a3B4="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdi5mwkd.0_1596679332756_0.8710169303616917"},"_hasShrinkwrap":false},"1.0.0-canary.kdi9bq15.0":{"name":"plumier","version":"1.0.0-canary.kdi9bq15.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdi9bq15.0+c62c8dc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c62c8dc2dda71d63bdb6e80a3d51157b8cbd45a4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdi9bq15.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-0l14d7gSoKtg9PxWQZSPXbARU6twugRJsmJ2b6DIgedS1ayZuyP53f813/ars2K0JkiRgDqJnTLACJXDbqjT3g==","shasum":"012741945265c9a61ee63508a56bcfabdbcb8870","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdi9bq15.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfK3zXCRA9TVsSAnZWagAAtSIP+gJJfNXQQpCGPh4kAI9l\n9nCOXKl5DoM0NoKYIKUr8Ky/PizGR5s8SztIFRGPBlJB7k7S6D+LMiY2xBgs\nXRwMmjO8R6QS78VfW99u5V0QOP1+ycrLOx1WX7xM0PEpd59/MYofszAnIPOB\n1H0DdqWM5jePJFJ3HE4ZrRT7wNgXun+VQyauKDXngvXX9n4f7AZeQzWjqyFe\nu2Ar8VRTmuf6WLBBmxrS9CVEvLFTu+3dMZHKaC4SWCuTSYsPw5bi/pEqdoe1\nqwvs/qfvESppNPMpkpu6X5ngEXRwdfZbs2Nn4sLHfHpvtGO8HuURmVgSAKGr\niyBY1jKzf7EBW4a6awKLLNZ7LQtfC/Er4UpfiZAFQ0zyRgW4j7uGk4jhpUU8\nPGTgL54J0vRX0Sg1mNfOylHPLSqRrZmmuZHDCcE42IWWo1F+hr196Ec8wDdM\ngP1cFLRkROadv+3WUP0KInyncw8nh1XBohZbpCI8UqzGZ5MqCjCZ3gpt69uM\nD6vEevUCA0K/59ooZ27G9n7nVZ+nj6oDroQE8xB8hg0RF0fzDvvdSSuJ+SPo\nvh3Vxxyp3zf/6RrZ5ztFGyhJ0eoywgQG+cJvEPkBLBjtKKV1UVpi6wUuv8md\n4d/U1rYUQ18j/vZAA/FuEqnMgot/vuXoiszaTT1/4cCKkgjuAqW/WAvJtFjp\noYPa\r\n=PfAR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBQjrpBQ82g9FBRjtJ2eRectohKKAb/ev393GqauANaRAiB0gyzfMj2Mo1B8rt6NOB8cAwOjxnB4CdLJF6JZLNQNQA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdi9bq15.0_1596685526645_0.0060079223658933145"},"_hasShrinkwrap":false},"1.0.0-canary.kdid6yqc.0":{"name":"plumier","version":"1.0.0-canary.kdid6yqc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdid6yqc.0+1a29374","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1a2937448a8288fa11bafe68f3368841cf95b16f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdid6yqc.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-pJzKOFhZXBVv380tqHwmvQuDST8SlUXgkvOET3t7jB4wXH7Hv5gFhSNelxJBdz5p0BXFGWsUIc5cgrdNVQc4BA==","shasum":"fe1d93b396357a9a4cb10f066ad6534da7e778a6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdid6yqc.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfK5Y2CRA9TVsSAnZWagAAvTAP/348IFTTe01b5tdU0BHw\nePdY95rWOZEbMcst5EueLENn8zINJvXWgyueuAHzDPidfnxPo0Mo8lkl0heg\n7T8iw4OnHbI3ncIkBgYptyI3RzzSJrTFr8R5RhlfNj2pmlqshR8rQvcNkkyh\naZydT2fDkOew2uZ76nMS3NZKJDazEHjWvOuYdXi0OjsXfTeH5NYzD9u1D5WQ\n9BZyKClgBbkK6KKZzb66ZGDq8+X50fUfIBMJp+1RQboHdsD+IrroPrncxE29\nXKthWdyj/N0vItbaSNUydCEp4C5QNHdSGp1pXifmdtWC7bflYlMaxD+iPlVt\no+YEo78zmaa/ZS2zobmKhhbz2JqCZxTTrP2IrbJ8EFsYtHc4s30w+JgI7yrJ\n3OQq9FnGihmL2nDrVUMoVYyC/b0Ghd+jgcci9+DfevYfpE2FcDmJCdA6EC0x\nW97w9Hgcaj8HtKRvXHnmOyz3+Y4CC0RMFoLCq1ZH256XV/PIcU7xwOfG3sUa\nQchQ+axZpu0TP0t4mGIZ7QLrxC0oF7zaUq7XeB7oYzt09QKlRX5VURUGesJJ\nVuoCFy59z9El7YaQKCFSOspD+WJyUwNntfoXcBxHrTj9ifiyuOgayAVz1vc6\nBJFbH4pO+ZPr3r35D8KAD5iwVIt7li3AeFx3WwnGoHtD1crDPS0KkD3oJRL9\nbXQT\r\n=wK9w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC5yNemmIMuM//aKqHvGE4I/7vV5PF86KkAn0LCbuDzbgIgS0/QIF19+X4RfxAWz2IVEmQYwYcQbhDT02/xL2N3+hA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdid6yqc.0_1596692022188_0.09233259801311089"},"_hasShrinkwrap":false},"1.0.0-canary.kdjdfg7t.0":{"name":"plumier","version":"1.0.0-canary.kdjdfg7t.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdjdfg7t.0+44b6b52","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"44b6b52be66103214c84ecb1d9966f3f48e3c0f1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdjdfg7t.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-UI/VrcvTnAGeKbmzint/n2W5h2mwejE4BWLAuT0OQSg9y/Jxwr2YJi2k6juxAXq6NaRy+pd2teJekeVYOdOmrQ==","shasum":"f3926b1e8858238c3c5274718998b9550f8ed1df","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdjdfg7t.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfLIP1CRA9TVsSAnZWagAAExkP/RM+3RreHvzPxCXb/R2I\nDIBfH+THFhMTEc3EMrxlujivHWG1NsHLRgt7T2DE6VxWXHuyS7R9zVHKbbCU\nA1VCCnXfKFkGtLETj90lXxVEHTFbI5XV30Q7c2WHyTZhnO+W1ctveFDVHlNI\n/cW53nXIFR4uWl+aXfIez3D1Vzodp1lHE7Yr4Gg2bn8gJPQAbZatQD632IDn\nNGePWpPhzvkOsvqBC/t2TaMFJWvTCycvCeLGoUADRaN6i/ZoP+ULhNaGkRqy\niZ9IA7ZJCxmNRX3AFUW4TJQ9nGAzUgwNykgHbsDElQIzFXMoebn7vEoj05zq\nmQUvYQhWcTGDGLHQxwEPTJPg6LE7mLxl5cWan7fV5gYBttpbiVHwTXDu02Y5\nGLAdHngDJUmMQTbmryB+FVHQTlW5oS9EN6A5LnJb53QbHtP/8l2NjNAICF0w\n7LfnMsPUGR/AR8cQXlftOSgC/BbaMQJR9YGL8wohhGPPeScu185d1YtVUaTz\nCCS9Z5x3jkVS0kixqfg6x7ETbl+H/GwFIxwG5YxHcUfrE04/F0JqkD/26dTJ\nV0HJfckNYQgx0B4cU7e1WDclg/BcaR4oZl76W0gpp1sW+Gehm0pPIQB82E4d\nxj25pl1lRaMvjltxLSjprn3o/khYytMizqagFPedB67Ypawwgrjm8hNk49Wh\n+/ki\r\n=Lp8H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDXW4asWn8Upk4EAlMQAD8BjrQgdiydBcfdAcGLFmF5KQIgVHOKH5WereTa9HCo9gQ3Qwt0MMcG4ttvI0t8vP9MDSg="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdjdfg7t.0_1596752884626_0.6356459960893881"},"_hasShrinkwrap":false},"1.0.0-canary.kdjj7hrf.0":{"name":"plumier","version":"1.0.0-canary.kdjj7hrf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdjj7hrf.0+3fccd92","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3fccd923c599e57b5a0efd466d288dcc948c1ef1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdjj7hrf.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-Rszx+DAyP6zffXMNK5pMH+NXK06RQDOzVzjLgetbfKI1x4id1p8lSEy25epjDiVZpWrNDOjjG3h4EwHjeNKXnA==","shasum":"3eee58eafe976003a7e215210ba9c44cedeb984f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdjj7hrf.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfLKnfCRA9TVsSAnZWagAAt5YP/3PpusVIgB2TJBL918ra\nxTYMBmFzhxBKyUONBPZaknnfz4DxugHnVra+bA8xtFdWA/3Osdx5qqbLBrC7\nkQPmkj5JHl8dJARBruz9CkqcVt87lBDNek5Yj4k7XNYDJZNNU5P2R8vlP8SQ\nCcdcM2BjY5RYcFsorDCcf8pMc8G6gt/0eHajzQ8g6W77Dq1zPQA+n9QwQC2c\njR0KntvUThh0zjilw//nNZDINYEekJpc2QsSCsSGFfxJE9uwi2UOzJn68UXP\nOCS7alk6o0BhJJi23EfVwYy8lhvQhvOX1nkQWtOWhKFPhAcca+JOIgXOUFs0\nGsCBu17E2SwGdD9r5fM2lsBFEjjIMlAHfIeIpsxflRu/yCTrfM4tUOQIG1qo\nptODylsd5s5QW5QHPUJPg4QSMTHhhiC9fG8aW+P1BDZVbihdekKPr/25h6Rg\nRSfUIWyxPFeqjmhF6bcFebVscm/l1n3i3XmdJIUoWFge83Xu4va+pAYlBq+K\nq6WMeOnBRjFDOfrafmIKz7ux/dYKXB8esmRvfdp5K2715nDzNjaAwtdSP529\nuE1s1/zHbaUDN2gg9biwUWMLRxkSYU5TlbHy2TwHAINagGgKOgF1I7pmXacB\ndYvv1sBVzG3nnL4s7yXjIXcSWZH/XUA3Kbp32A5YVcqoiwC5YvrrbP0Qm9PX\nBbb+\r\n=F4c5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDWMow+d0URjH8iFubx2hkgygp+Gw6pGIeX2c4uuevBQAiAyjYS0H3DwLNHPfx1vUHK+jczv5Z3PDYTHDAO9hq0glA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdjj7hrf.0_1596762590603_0.15330053375411246"},"_hasShrinkwrap":false},"1.0.0-canary.kdkqxkys.0":{"name":"plumier","version":"1.0.0-canary.kdkqxkys.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdkqxkys.0+f21c49f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f21c49f710f91bca701fd71a736769b90094108b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdkqxkys.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-1Mr6yUXpPGrMbOvS3O3uEMYBJ24Qm+tBsih3fNJB0Ie4l+cxmANjy29UjQbUbpCEEk8TXE4ulMgaxU+4T9yT0w==","shasum":"d02070d6b6a63b2846021a0d5e13b50980124def","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdkqxkys.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfLci/CRA9TVsSAnZWagAAUpcP/iL/8l2sSIqlUnVsxCY2\nrxhN0Z6NmfM/vOU1vkDSVOAXQ/9PnRtBaC4cR2VHbETVfxSkUAfdBH1hQ9A/\n4Xk9F16rejphajlFD6ZNy6m2rHVNlpvZMt1aR8Msz5XVyzgW5VOG4cO5Vlpe\nn5y8tnxB89suy33CMgWI0pJvVVG20ybMqZlkkDqL66UGJLQ3XqBiL0X1GR/Y\nRZ2TBS0r0CBary9oo7Nk64xpz62QyWayBKkA1S4lqCQju1GkxT+TJS507ZGP\nu0dSO23BACbOuPDT4ougGb+WOBI3lChXcJfuHjtYDojs5UA+wk6FB9EiJH4r\nHubhk5GlCEsRXXrZkU5ObPIES9aQIwfnQJ2i9d8eHITn88fPylG5j4mSqN5W\nHl9OcS1VnlPQGp7ZwmRvdvUw/CtdJgFte4NmuXropIg1fltr8bJ/6eOCjQEo\nCo3mZ/EtucPpEWptUHasORvu0XYIWm7czb6Tfxc8pGdXl5Yw58TGmp1YgYBw\nwDaAb0NmenrMP4q0J8mTECdJifiVXwwrN0mQ3D0j1w7TgpNkUHW4XWiOhkiJ\n0mh5Cl3fCsVkw/uJoDYUSxlp03ByhdJfPq8OZFRgfHBXdZqe+ZNIh+PKLOnK\nC3xXtO6RFsTOqCvS3ghU3PGC/jHI50RQPV0YFbmrxtpgUrVLNBvx7cgKnsjv\nGi1x\r\n=oUs7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFy8Z79UmzvHGOouFaV2IgwGbTxUmBPOBX3I316KgPChAiEAtXZcGbI079wDWw8tbk3H0xOV66o7LV0v5Di5eamXRrM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdkqxkys.0_1596836031458_0.38486571586468066"},"_hasShrinkwrap":false},"1.0.0-canary.kdqpr7yp.0":{"name":"plumier","version":"1.0.0-canary.kdqpr7yp.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdqpr7yp.0+7907229","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"790722952d9fb62b0cc63c8f987cfec27f59b677","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdqpr7yp.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-+Jzfs3++kZyZnMzhwu+HqhpIfVxEAHCGjAXMOF393MUq2jtOLGC97V/ClcdMYBmfvszcc1LYqCE4dacyz7pjgg==","shasum":"1f7fcfed11d5ee23ca31c7ab9a7fa615f378c30f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdqpr7yp.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfM0oyCRA9TVsSAnZWagAA8wsP/2Z8okEzhi1nnI7g5jx8\nHvifr6ypjDzrVcKp+NmvijuHUALhu91D06jmAE8nKJ0WqDOCZWJIKFZ78ziM\nPevcIBwq33AZdblUh2X7vHz3+n3Up+dfB+EoucOArqqZDtPwxft0rw18Nj68\ntxm8GSZEh/98Y5QFBb24kZX94dkaN1FjTzLWYr6g3gj5DBiGUj6s9YSA8doe\nxPFJicosudWpX+Zjt68rR3H6Epv7lNutYOIn3MovlOZ9q/v4U9Lk9hsXBLbX\nAx70BephezJV+alY0L/AsfoK3jlARNIRLn0ca7ZWKt2694wPKdHIxVACOykL\n9nE7ZvlhaZV/KYGBMLJBOLkseS68cOY+h3jxH8vlUMuo2T0Wy+y2d/MXq+8k\npQz89fRKPRo9pZPh2xn6Dc1Tg4ga4IN+a+H/ezYvoZlSGVPmPoOk9vsvMgRy\n4maoJnJQWDI/MmrKKKOL8w29NTg1dW2xNsxFHz2mXgzcb+im5zE08HbiASz2\nuW7x6x2B/VQXL+XrKFvXVkPSTermTl+5ftT1RypzsN4GBLVmd7O3Zr16mjjb\nUFCapy8WMXLXDAR/l8N4VLikOIxxv8L5syQNzNCSciidb2oRNQQKq8jW1OkM\nI9I0Tx5NUhXB9i/0RWl2h/i4hTr3l1+nZohm4JVxOsM7UPXCvCORCKrNB213\nUbVz\r\n=+Aok\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmH8JfLnxubK/XjLZB4ziFHG4KsaCeHFfWkkl8ECTVFQIgBVdt2HmexTmBVwPoI9e8EzfjK7v6gaJ7pWBwhlbSD/M="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdqpr7yp.0_1597196849935_0.867376446255945"},"_hasShrinkwrap":false},"1.0.0-canary.kdtej428.0":{"name":"plumier","version":"1.0.0-canary.kdtej428.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdtej428.0+5e8868a","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5e8868a92147d2ae5ed53e4c7f47965db5b23fe5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdtej428.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-ohWzJjaQ55uVTGpdzUZ8/lKIBoMi1+9bcMhIm3C5iawucg4wtD1uSan9ADiUGWil/RfSNIt/0614Kz+qUaum2Q==","shasum":"ebd7a80bcfc3bdc7d58b99a699c7d94436b20179","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdtej428.0.tgz","fileCount":13,"unpackedSize":31367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfNcUiCRA9TVsSAnZWagAALUUP/3nn9bnKOukuhVldgSI4\nYMJ4iae3i3S4zd5CPccEK53CCtg1/OKsVwM5+iOcDuZzrmYQV52AfXZ7owWl\ndYB4Pe5JrAhyqFQH/q2cD6Q2zQ7/DehyUma42ktty2tnASPsLlYKxyzRnJ0B\nm412VCpXTJ57DbGdmjoJKVRK4UdFaQXCrOLIacpr8A/TFLbybkeLF9URb56Y\n79dAUMSayY6y/FhMYAkbUaZxD00hh18+WJ9fJIUkyJFpw1TFHSQHZWRnA+xD\nQKLFjlOSlQx8MBQ7wTLXY6Jk13CKSrv7prDxSFnGxy3XnhTU8tzG3Po6hcJX\nle0Qea5rhWOkg5K/wcVCZU8eI1JJu7992UdLfL3vK8zDvYNaOxIryNjLDNrp\nGGaCmA+OxtDracFHS+AcUvlF1i/6EtuILbs4dXlRlfR+43SF6y6g+L/y7ap1\nSTLugZG7ATBDpiaaunm6or0bhfOT0Y6RmP8CqRgoEMAAavviGFnXtSw0IJfw\n6kCOGNjMLRE6sHjwjzCUZZE1GFJ0N8RdqeSoxVYgbA+hdLUpv4Fg4hSjGl0P\nSrSpks3EzJrJAGdodd8BaRhKRkx4GWtbwaXZ701Vhav6b7+SvcTXTcCdE8ff\n4quXdzQaY9z20352ecO3PHhx82oRT0R9bbNYmExTNwa5JjtglX2H04odS05t\nhvPN\r\n=oeWu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHACuudNIk4wWVmN1bw6kJU0+Uqrd2AHPBMqqvO7LM2KAiB1caWK90VSfjR03Lij2q1Wg9L4Ac0cS+8nKufIdp+lGA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdtej428.0_1597359394456_0.9231419766529785"},"_hasShrinkwrap":false},"1.0.0-canary.kdtibqoo.0":{"name":"plumier","version":"1.0.0-canary.kdtibqoo.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdtibqoo.0+14d35dc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"14d35dcc5ad081bc853124a3cfdcecfb924b7973","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdtibqoo.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-6l+70AJAsMCJBRhZ8hliYwaJXUa9tByU4Pu5tSFjzZHIks3IV6QLT9sfnSjhsub2B1dRwYniZ7EMJAzNU+te3Q==","shasum":"bed12ebc1cdba5a780a6f971f8f4d56eaf02d65f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdtibqoo.0.tgz","fileCount":13,"unpackedSize":31507,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfNd4KCRA9TVsSAnZWagAAvUQP/1XjeWAQdbKD+9U0s3TO\n4+O9vo1OS8lxT+p2r6NP6VKx15dj9gy2tTwcGaJYBZzacWTgC96B24G7q5a8\nurmsVRTcnXYptUeycO8cjADXoHmIpBY/pV73owqe/Ga0/vGrwwso0nbL7N5+\nzkKFQsopeANmtcslSnrn9UyVP+6/W3KuRVPflEhNGNCzoZqdDpHDkzfm2MWo\nZTPpnpbtPsvBPfSpFLXJf0xoZD/k3hB4fn65uRxTZSDsvwk96jziVwUE/6Of\nuSKvqNQc4swJBYKuwYeQrr0Dfy3fcKqFDqjjHYlsnb/S3LcyHSBED9K7+oP1\nnHMHmbTmvSqijrh+tCm/09zgJJ+VEAGU6QG8q/sj2ZSrf641o6Ad/KajWyy6\ncgJ/8xeU1QJq7PZ9cbhtDTgtGxz68GV7dpx0+q5TKtlosmaCTMgc26PfYo1z\n3kURDi4TgX9ud6XthStWiUvm3WoINbDihrVgPfwcCbpBdxV8RIAfzkTEo/rj\ntkG7nZaEEmLIyrNZn+v3I4hTjlG4RK06i5BNBTUMyAv/grNxyqk5LvLOaj7k\nSQoC16brN+PuWTwdBuVJr4Rgk5ML9CEhJfC9loKvQVlmS3XJ/TVFcnIWIfp2\n4wH5W57xa6DKuA3UHumczK3cIeGSJes8sIJUXaAxmBYhwokzYmP8BwM4NHme\nBEXo\r\n=CdnQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDR3iGmNQets5StzugaBbs6xqsohqC4lYgh2l+rrsqUyAiEAjkZ19Chwa2bDcr5uEpt9SBZHR0Gom0HBU3GJE0XT1fA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdtibqoo.0_1597365769477_0.8997525709141163"},"_hasShrinkwrap":false},"1.0.0-canary.kdvw2za0.0":{"name":"plumier","version":"1.0.0-canary.kdvw2za0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kdvw2za0.0+3ffadbc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3ffadbc90a8f7b14a6127874df90c13237778480","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kdvw2za0.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-SpO+1o22JoHGUQMe+Pv1HfPtJtrVvZqVnqZe8euSVQBPPLPhUQPMMnBL+fMhr64vPty920wtaHyjHhy8PzmWbw==","shasum":"f0228fe555000829560d1850ad89432072fd688d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kdvw2za0.0.tgz","fileCount":13,"unpackedSize":31507,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfOBCzCRA9TVsSAnZWagAABq4P/i1ps25M+fBb/UVfh+Bq\nVnLSSAftBjIApGOELR93FpdQ6cRl1z47bH32EXXpJ3cSJp5pD3qMgOK/PIAA\npaloRMUTrQCToHP3iV1xpBKPXaUwiZx+TwMLWx79BNzARuyxY4gsA2Zsfo/q\nIH6eu41Vi3+HSjWcqsrjNnWa+WUwjQkEWER9O2Tgj/yOWeoFVPr0WN1lQinV\niQJEhm/7S0xnENbtDbWThaulgj/DyvZmboAA6QuNQPbOcEJJtLWILMhI0afr\ng6Q0Y2ED5/BCjdXqqU2/t4fn/c588zFFmF+V+DUzYoVS80/IKGvvxbVzThdK\n6p9i5Jg5QL+hRK+hqkai61D/HExlGJdjEeZedP3ZE56WwwAkc4+uxq04zBJ0\nZZzRDyYMcur+ax52dQp5lJC9t0155FLGskjhRyGs6wXBDINW/w7Xkpmd3ykH\nB4XeYEOSP5FGmE5jQrzQIwyJ+V7uTwy+LvfZQpIrVHM43fY7I4GOO3yy08Nm\n8lqR1A1C/tIJ5pqys/P3LR9eG6IDChNeX3GdSjo0k98MfYnFNpWAuQMW0uW3\nQSB541yLgWehFtnrRoYqruw37+ti8kWOzsrDXdwy9n7pJrlIoXYyq5enSWiC\njRAJ/Sb/LqUnyiQOeRaUNmyol1cJLfA+PPJ5tx3HbxdbNLip12XvKCrV7/at\nvA31\r\n=EjpM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC+haYOIqLrpA5woeIuFwCqJ6sHKU3YGj4NIot3SvWc/QIhAL3fMmWU/fHff8eGnc3VJqviUqLqRfAeaKVwXFikYv1l"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kdvw2za0.0_1597509811170_0.5640181115445488"},"_hasShrinkwrap":false},"1.0.0-canary.ke8zqwdv.0":{"name":"plumier","version":"1.0.0-canary.ke8zqwdv.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ke8zqwdv.0+adc4afb","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"adc4afba59c808b862403bc0325063302cb05b3b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ke8zqwdv.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-szxnFilpl5+GZ6TlHeBqktrVOMZPQNMT1zDaKxmLfra/VeLQCi1xEq3I/SdxFzcI2n1BAZ64F7sqWAdaG0OfVg==","shasum":"2a20df60166f77ad49bb04a243e70120d4cedf36","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ke8zqwdv.0.tgz","fileCount":13,"unpackedSize":31275,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfRCdPCRA9TVsSAnZWagAAZG8P/RkBlHpRJB3y0tZQVw7Y\nFXLYeb04jSd9la8kMJ6gX1hsS7YDgEiCxUePe57dfZ26xXvdengGszKeGhcV\nvohMG8hFq1mZIlAHLYsdSgNRsR6HJ8myBqVk7K4odCcOwEb2h42ogZtmB6pc\ntmAP8LHYYfNuS2ChHj4a137vFwfiatemiB1yhoZQcVaKlZOzh9Q0b3l9nJMR\n7cdchU5CDTJQVKyA42iKJRjM09RPkT4xE8n+HCaNTIEv3Ltd7xqnW3C9cwpM\nzISoyjlX4584bmoZ7Dcyw907AKc4CX4YgyVd18hs9sP1NfzfgHQEaM84Hyfb\nJ1khOPMpvro9a3dNJeSACTl1wYlEZwCy5NkNtlLtYBJl9DJcdWnr2sPRAZaN\nmwnRM8KAUeovkalQf+IPA196NKfGdBA8SlkKKN28vzDus9TNkrJFdWdCLPdZ\nikO5pJ0JJTqrVTNvzh/F0cTAzOe8lDqSX/cDRNIgUv7vdZrikq8Z0DOTaU/k\naDisvqXUpHXN02Pv9wJJJWS76IbqC/xE7MHW+Hcyb/B86B+EG90IK+fvWKFF\nXsC2xiUTw2kMf5poZv/DT6WMslho8HjuGj4gEvhFiimGmLYKYfVNq6B0NS1l\njudAnNh00UKZ0g4T/dYbz/0+Zf3vaoApuqyGefuQ3x/nzikajtjTHEEyfFQN\ncD/y\r\n=jXqi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICqr26AMrYkYu7ofl8ezNDeSypytOUI7yrplCciMYcA0AiBSlLWjUDK1NjdDgYTsbLUR5PFjvbci56GdimasPgC6pw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ke8zqwdv.0_1598302030675_0.979685979471117"},"_hasShrinkwrap":false},"1.0.0-canary.ke91wrl1.0":{"name":"plumier","version":"1.0.0-canary.ke91wrl1.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ke91wrl1.0+e2c6cbd","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e2c6cbd83b5c0ded22f2795b1b8dc0876c9b7941","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ke91wrl1.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-RI/kGuIjcr08BI/2I+B+a2mLwfdp69SkW8h2aB5CNteR2qDc5RIJFMJICzNwXdOkzYx9wNzazXKeYqp3HYRjEA==","shasum":"f5746d463b0bdb1db2efdc5b6e6e999ebbbbf02b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ke91wrl1.0.tgz","fileCount":13,"unpackedSize":31275,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfRDWLCRA9TVsSAnZWagAAlIYP/2kIpgYZkJWY5zFZz8C9\nHyBhznWdbG/93oHp0sLt6rwvE6b4pJMXodxNvbGQk1O9rK3yxqnEcJg1AOaH\nQ3G4DwGjCAD5WzgCYG8CbqJfowf/0GWHWqIW6XMfO8NumCC3bP9A1YOO1dGE\nbJPBw8lo6bVgsasL/p6IxOAOILDcd6F/dYXYJ89g7VpUmT52Ko3qOiFI47rP\nKdOczc3sUX7QBl+fnECkti977kJS5IvxtwLzdbsRvw2aInj22EtV7k0fqNi3\n6LaDAY0cSUxd2W+Ph7I8Cbw84zcPlCYTb2yD5NTTSeRBeAF7ZXWRgIijo7Tg\nozJT7LUA8KU6Kf3zmBSnm6WB4yb84zDSqGUNepZ6eG+tMnddhef3OvtmkfAf\n1ZTFZbEKHFgRBL91HcQaBCMtz3CQm/DCSuJrU0x5U85yXW4DFFapFfL/rNzn\nMeUr/2mHD/JRcE21/zMOp3Ggtu5pStx4L/E7kAx+1Z2qzrrgw05iYKt0RJZR\nL8yVz0G+MuX5LyaHzJ80yl7+PvJ5M148TEbqk9T9+BLt8otxEz7EQCT//4Pk\nxsHwbngfNc14lFsJEKepSHgbYZNR2nQ7MuWXdHEUiSYsN5kLmZ6aXlKdatep\nwMB9WQjc4+z1bFyYJhTWTluMsyyxx7FckQnsQ10M/Ptww0r1LG4bTdPkNv3f\nKByl\r\n=2KqH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCaMugaDKtnMu53UeI1OxJs3/04PNJ5mLty6lGjPJyfuwIgV82dutAOHael0otrnI43LDFpPnQ3fCJM4S37Cup/9gc="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ke91wrl1.0_1598305675341_0.25947074221035815"},"_hasShrinkwrap":false},"1.0.0-canary.ke93q61k.0":{"name":"plumier","version":"1.0.0-canary.ke93q61k.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ke93q61k.0+15b2cf6","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"15b2cf636a45111107cf6c2579e1d72cd105b4ed","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ke93q61k.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-dHaU2+s1/Hy2+zn8meNqTzAM+oc3aLPOvc5jnKy1M3ILbiBoWw7CLf+3tfxFhwuSUc42MAYGltJ2fRz0hC2zoQ==","shasum":"09cc29a366829405f91591e4b0b6121448dc9611","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ke93q61k.0.tgz","fileCount":13,"unpackedSize":31275,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfREFmCRA9TVsSAnZWagAAD0YP/jxytMjk8lNE7vRB0g8l\nJlrWG14pFCzey9hotRNLjmD11HeUzICUiT43RB9uTZv1PW1F+t0ZsuJqEf+9\nfN5GpYqCgJlA8S7rLyOD1/NtWjHQx8tsDADFGTUjA+mINMMrqlvmZw8rNlBH\ncZn3oC6HslwjLHwUygMUJjrlARtCa2//l03m1O1rJCfHivBg/B9QWnRidhph\nmyVfgkwK9qREkn2MF4OqnMi6CR7a7RUKi8s61wzinuE6BY//rUnzOcG4w5uK\nqhwrEWWdWRb1ds4ML42JafnrWn3xWskXGRD5XgX2ZScULw1Nd63QK2rAV4QB\nu7tArm9lKOEN2R3nWLll43D9NBi3gHg/9AKa7hklKxtBOPo3067LDAQ9F+26\naigSERdTi9rlSrE3t5+cTrjDSWzclWrlzwZatuS4wapc6wdtoN/Ec0Tx6Ho/\nzmsP56lPhZ/UvLhDWEbYjrzhRhIuMj615cndXv7PPviFEFhuQ9YGBrhwUUCK\nR/gAvy0NG43y8iMsktNofjseE0efNn9KMK1SYJNmZsP0DjiA3Zx9yC7ImRhW\nq+U6w8B6KDnWKFir9e87dtRHxPfjfanq2wj0uJO+sTLx+sv2ja007cDBXImo\n/DiRPzquiev3WVE8Sk2Pn7dSfSE0FwHmSTZafOCCvLe8+CqXu+9XCVxZ8e/m\njEmB\r\n=G6wC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDfAkl1Lx2lZX9Zvm98tJl4FAU2EJrcLve1PyBxolytuAiBc5kIAe3eJCGgQy4tG/tK4bKjvQKe/I8niL1BHI3HVAg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ke93q61k.0_1598308708790_0.6727656059085567"},"_hasShrinkwrap":false},"1.0.0-canary.keao8m4o.0":{"name":"plumier","version":"1.0.0-canary.keao8m4o.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.keao8m4o.0+7a9b68b","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7a9b68b6b6f55cab312754dabf0d2ac52d8acf50","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.keao8m4o.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-xPh8lyuANGjphjZneYfNCEM6OYBEsj+6ExNcD3oali3aQnLoYH1jcmQFVcpXoq7290b7RV2fqrO5SeiG5dmKRg==","shasum":"3646534679356b383b5fedf130d053e0f3d327f1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.keao8m4o.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfRbQtCRA9TVsSAnZWagAAYnkP/1CBujQmEoohLG1dIjvd\nuxQc97U9x6l+ToLaNQV53fvBsGLYPdT3wnce+yuOruDBcl27nEAstYQpF4q+\niDMpo7uf2Qrico7cq4xkDfW5ZS3ZSEtpiqprx0qtj18ZjJICJ9ybAsgthoZv\nXkGEsp0a8SlzTQDtFW5vZLe+TVrfDZNFpDHkBBlHobRIS4F6s5jOIWZ+GT9G\nktWQuktq46wLgzSxP+Dd5BkljSyVEStNaQNfXIRaYuHOJUmy7D1m9vARSCPb\nlOrcx1bjRGWA2IU/vTlcmaElyrkKIrX1C7EcapqUt25FVfEvvWcH1TWOLIjI\njXbo+92N6ddYqepb17uIJrbKSJINMVMa/dMuSA72+/til+7r9vRKp73S8yJp\nljjmvij9LDTEnoIKGZqtPlgqHnJkWhPIae80Rjqec8wVSoMVf+aZofFDjwry\nC1FzPadUkbP7pTivUMiYNdsa1UjVelBuvPQif0OwbYkzyLlO8HyxTXKosOvF\nu00qC0RXhvsZjmD2s9tRrThp7gv9KH9FSPY5lWyEFoilmedrYExJuZktTFEd\n1sHXFDo3fVFVbCx3V6LZSpwAYl51VskTiAMB3Bu2EmeGP8KDogS6NiFuMyqC\nWW1KTThCFyMCThEdKzUr/kOpuLhkyYWeUSf1qJdYnwvfl2VM2fbJFXyaph8I\nFF5y\r\n=jKNv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCiDtOLAC855QXrQLUREkqy2q4LTRkY1OvWxRoSvvqGQQIgQZct2nPo//FoPaVKsKNqqJkAIvBy7/Ai2g+Dp23bqlQ="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.keao8m4o.0_1598403628815_0.5470477525425228"},"_hasShrinkwrap":false},"1.0.0-canary.keaththw.0":{"name":"plumier","version":"1.0.0-canary.keaththw.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.keaththw.0+194d725","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"194d725bab3a2f0096610bb9cce506fd2116ecce","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.keaththw.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-FQKjLuSXcenRNyNLjo4VXvtPLygg32MqrUVPeYgnqzZsClIQclU/jpAZ6G4nLOjHGSPDdLaHySRRcM265toi2g==","shasum":"657dc0a24b22114e3f274b037d7a3754a8023a70","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.keaththw.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfRdawCRA9TVsSAnZWagAALX8P/0iLeDOeSEGqddfbxr2p\nVDep+agoXU6Nndr2RZuVBOS4y0QFdDOStK0vQ5RDfALwABEqOZUeVaxj9wnO\n0RHH3mJd2bGB5mWHxmwESgLSYRgshSTXTglWGQ+78j50OAq32A8fvXaBuuuz\nNoAi3UL8opoVqYUW1pcGmW+H8lENyyYViE4gCZWS3AV4ySIt6OdHna5zbt0o\nAyaWnFpZSNlm5ntPY+1ELWpmTvwpagwSXstWTYfWHw+McUzbcLpvVzfHwyUx\nTv39xtV5uxumZCJYDbegww/t4zlp+ab4MCA+Jc4Fx2NhXCEpDIA3fOes2CTH\n7GUc9lTFWKv4I/9YRcQkIECjPaJI+BKwQDuYkG5DFfZmYKRTpv/dvM4AWHQj\nC2p8sMdgOTC5DER4/Xx0w8XYNbvP5U3Ks+KovNPxWTW59Pl7DpqhvF/4LIk6\nQ8pk1RHoE7Zwz9JayAl4QmXGEsC4S676nNpbsInw5N7xKcap+gPH0guhI+kJ\nWXXTNwPCzO0zL5qy/0JzxnXZ8kDwyGA7GQWgJulzUAzWZ/NsFKvnPm/ElgXZ\n0TGxFx+/LWtBONZZBaDk7nabt3uAnPa9+kcu7RCdnLP47Nj0kfiisoJOUxYe\niMRnr3y+PM5KOgj3MN4qZtgAANgXCUdC8Gznb0jwalY30qnD14Wu0sMOL5CH\nKShp\r\n=/Y1N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIETRMUL0Z/zU4hSPevmVrDXOLEplXq1sfdY0VHEJo8DKAiAI8FS0ZpK5K3XpWxDsrwEPljzGEEB1mARLNQRZ3fPxEA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.keaththw.0_1598412464389_0.5683729546461893"},"_hasShrinkwrap":false},"1.0.0-canary.kebz3sj5.0":{"name":"plumier","version":"1.0.0-canary.kebz3sj5.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kebz3sj5.0+60e26b5","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"60e26b5a715ee021412ed3c2a6d1620932877091","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kebz3sj5.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-38G7fuv4eHfvVNzaqSQVLLuhcKO85FvPJC9WKSX0BcbFJfroJx/GYn1q66hj1LbQ7JUy81xNqnv8QiunF/NQGw==","shasum":"2d2d011ff68b854864aadcc183ad6e2541a07df9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kebz3sj5.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfRueqCRA9TVsSAnZWagAAfSMP/0ip/KMbqodZ/ntd0PEN\nL0XFs+C33ryF/hXSQekQVtkgJIK5pnT58/rj4puT+CcEO2/xrvGBeeC+cL8C\n7dTVOu4lU38g1VKnfqHHM0WrGlXMaHFYo+9jRtii7LTQLwD5e9IoJDGxW3YE\n4etKySv18+ZYFoWy9jfjS6uSNtKXRKIA+cNS626jeC9vh/9/ZmHHckycWtZb\nyI017CXHmNOMwQcTcMzo/3XMHqQCCD2mwFgGckDfgRNnAx0KCDYxFc6OZUS4\nsuxHAansxCISzkDOZDVYsSSzTWyzBc23IvLse8d9W5WH2FKlJJ2FtU13Kekc\nAXTsZDbQy7NnI4ahFN7r3x/MN+lFFQrjAbdk62Wxjx+4ZrVkMmPkTQ8CRUZ0\nIa4QrJ8227A676uysm0muItvS9LzEP0rOgQEQoiVseszZzXjHunMCopUdJat\n9cSykLa7sGqBmR7T0BUf1GaxA5W7gu8zkKJLeYG18m51lUUhpCfVN2siBdRf\n94HoYJJZpyOqPwpryhrhsIq3k9kjqLg6/m9Q/2vsJxP8k1CpBhjQz7lYJaEV\nxZA/j2C1+dr8vuu6bjIEtAFjGdH0ib9M+skrtqWJVwBvSV+sIKQcM+KQFgd8\nc8b9U3q56qdZr0n8Yp/buNacEj0NTWs7L8AbJVAObB6qNrrCkwhXmvaFd0i+\nf5GT\r\n=g32R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDkXSore93vvRhCYeVUdNtdvMPcZSSGiPbnMsQlfJFuRAiEAgzEvFesdjETnhOQ+cztlk1rRqOz4W/+oQ2MrI2j1VGw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kebz3sj5.0_1598482346244_0.7937659670206016"},"_hasShrinkwrap":false},"1.0.0-canary.kedxkqhh.0":{"name":"plumier","version":"1.0.0-canary.kedxkqhh.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kedxkqhh.0+fb78126","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fb78126d2a9243dd16903d18d7583d36e3a7cc7f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kedxkqhh.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-HWXUQR7v1CjS60/t7aVgXhdi9NUpcLcCVXaj50rwB+2gW9yv+E107xABx/WusAsXgRqz/U3yhrA7rAYW7GN3Gg==","shasum":"d2c47e99bee95df0ad79050310db64f03ce5b2aa","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kedxkqhh.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfSLYICRA9TVsSAnZWagAAGm4QAJAj5gLK2inbPPUw7F3n\n2cjrdvvAbvYGmuCl7Xb5qiD1pU0r5lmbTp32as9yPK/NVb5CMKmlkUQqbNMN\n2WpIsk/H6JkzIDGdSeqFcWqaNnVo3MzftzhbHtEcg+iTWmBNU0w5bfG6knaq\nvA/27sERF1pSka50IDlylDG4X5wA4aYIo/DSyxSHtJ/Y+bkgUhhGfoFKJW0a\nw7YcubbrEDNSfEiGhwh7r4rce3fJ4g1UO6ACsarlsSKbx6aCgRTqDXSiWQ3O\n1bZalAviEX8XWx5u9aMFL90cX0JFkyrCvHR7lvmtRiCG/vpRBFdR9JBJcItf\nQfA+Hj4hrA8/3ni0eS5zQR6bhNfFjifUvKldnNJsHMJ3WyezXqvJZmC5ZnCo\nAy9Iyq8701ZVJPJsr0XQUVJCXCac87gIZMMlTPKTmM/F7elaCux3VdKf8zls\n2vgxUaM2RNpXsJDsAVVUQbceystRTejW8LuifZMQYoM+jv54Im4iSV2ljkF5\nXPlKJBvBqwfpE3Q2mBfZpmHJN25U1pgN46UEEPjV2OfnF4Vlap6nrWSH6QQY\nqoJsIfMFQFk6V2AfNsnJJvmNjQS5pyts6eDL+OBQ3Zm4knNQ9pjvumJ3lhvW\nnispB1Tsqs9mjKBMg+lECaRsPGvPR8Va7bWzcOf4YneGNeyy0B64dcajekmh\nniUv\r\n=2G9v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICqfzvgqWptt250cF5MPUr+VF+2rCB8406GD1pPV7+pjAiEA/bdpsSg9sEAJLA58eC4duZuMFVu9wI56rcn4o2Pqahc="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kedxkqhh.0_1598600711686_0.4908218850534656"},"_hasShrinkwrap":false},"1.0.0-canary.keeqwum9.0":{"name":"plumier","version":"1.0.0-canary.keeqwum9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.keeqwum9.0+045aa51","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"045aa514dc4a632e41224220ce089e25a3c78da2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.keeqwum9.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-ZD1nWckqcjZ7fvC8qcCmvdxUECbp5Vrznm5ysnQI878dbjMo0aTE9Un19BmU5pspp2UwP6/CmSk8msnbx/tFhw==","shasum":"2c1a5427e49a4262794ade71633ef0e6b9cec855","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.keeqwum9.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfSXaJCRA9TVsSAnZWagAAeSsQAI1Uo1DRytSzHeK6BgQL\ngN4zjmviUnav83AYC7lt6lJJCccb9TKtRYnr51PZuUdimIFg18eDVTc0hGe7\ngWwbPv+fIevGhk3er4Fo2MEfWCkqO0NoRyuxmREKP6LyaQxGsFJbimufhUbu\nkuapKnIqgHsGCt3nq7dPRogbcK+hDiAX75yrs3RQFO8Ak/ctaGrWZb7AO1ca\n47FKKo88HiluwcPAWYZrpRgzJiNgT3vRw/3a5S37fptqv7wJn8nTQm6LFeId\nyxyRaKDmlgw4bYyTnSsZQFakrMNIzNOdice+7bbo386enIRuZ7W1nqK6W4ST\n1UCXTrxSVjKmECOz4KdOkzazKbpXEBVHUDtyZhod/lCU2Fdp5Fs1ZDanAXwz\nrccWzpRGSrC7ECdn26W8haeXIiXJnb+gnLwiPKCCTCCz6o0dA0YrJzpY5PXI\nG2ssFgORMGKEwrRod2u7DEu9pnYOSKeWyIvOKHIrlUeXb+LzDsPfp4GwmnA8\nU1aWTLC7PvG8Wv942QEPzBk6fKPtOSfKpV+j8gZA1xAWg/XAYFCKS6rgyoge\nJr+Icil1uSqGNlurwqKuuHyVhYVt0eMEG85NDaKzzMy+wwz6Ecyy3vY8V/2c\nko+XhlCEFizWWO7jxuVbN1k+ZXMWlP0EL5WdDY9sA/97oD0NAzYS2E7MUwXV\nIiph\r\n=6eRE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGz7pJkLCGW03aLS1qCcIWMowjBAXw9vSzDOFH04AB1WAiEAyyoyagfzLtxfZcD/JiNy6+hAPJ61oplGvWFyqFngyus="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.keeqwum9.0_1598649992525_0.4881508852109038"},"_hasShrinkwrap":false},"1.0.0-canary.keexw8ts.0":{"name":"plumier","version":"1.0.0-canary.keexw8ts.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.keexw8ts.0+2a3ba80","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2a3ba8009ae6fa7633a126d65bd4749acac36015","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.keexw8ts.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-pfEyRSxN4vjHprCXQqUcHyy/Yf1mBJ+hHFk+ZXOK83bKlEI0FzXct0nN5Q30pvPkRqb79SE148yFOi0x1f+BCA==","shasum":"37ecf441e716372aacef86f35ab9443e5a733c34","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.keexw8ts.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfSaRRCRA9TVsSAnZWagAAiqcP/jsJBwk3qF9pLEygqdua\nZ/SIr8TJHs3hO217CcQGSpbhfP29/8s4AMOIQnGr2clYSa/hCvh5BNutWgGP\nvS5Umo6qp0JMNHy/Vx97EwTLwGx6ljy48HRCBvOJadrO8Tug0i5WmELxC0tm\n6jmU/zkkD9jd81GEFa0ZlnTJDQp684/KdcdCwpQU9DSvPRX8hZI0p9DpUNft\n0R025rAQMLp05ADujN9AJxkJOI0ifnsX8GEpG9NXIRElMMhdpML07RQl7TOT\nSOMzfLCjIRbmtL4XAVqTYAqpeaIZiXhvsepLClNIHscbDJ1SkOGY5HgrFeFH\n75fG65xCME902qzxMyl6a/E0IQBIJKiWJr+o+nlsZMyO8gVb7zLriwNwqFOW\n6jsdSUQIh1vZSIQHjqi+myJAgvnA/5GdcS6CEgy/y7d0RQBKQ1FvZf01/F3c\nBiKgMtKwdKeOtNxF4QGpepq3uPBlv+P08Wyfs+nd5phU+BLneDXxdc4588qZ\nRXlyT8nzR1yPEGrO7E0gDB1cpZoCgZdVSu9SvMKXFQKtP0S0FAd0ZSQlc2/y\nb0bM3A16P+xE3z0KmOYxJ/JapPqUijueK9JiSNfxGFI5HbuxL7/r5EkFni3x\ngR9o84uW2K7/cjDBVsOGIZZAzeGb7cqfEIUIJbfPOSBZzVSGTJtALP4YgDP0\nXtMl\r\n=AZkO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDe5G3mEZGGXwVanxEsYJ+b8EVBODuuMOLvtEc6pHey/wIgS5RPUx/Dt+YTu52Qc22R1b+gLElQrWbXsG9EEFLPwug="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.keexw8ts.0_1598661712788_0.643828902030358"},"_hasShrinkwrap":false},"1.0.0-canary.kef2e3lg.0":{"name":"plumier","version":"1.0.0-canary.kef2e3lg.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kef2e3lg.0+10eba12","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"10eba1274e8e553ef3cfe19f131ae213ac9f752e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kef2e3lg.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-646+s8DI5BB4fC0gXnY0di9ohYqM/k9tnpNG9OETXMRgdpkx/oKY4TKoH2pMHYKH8U1z/b6OcvKZwT5Gu0b9rA==","shasum":"9ee6855d9a6090074754cc96a77af851523f7210","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kef2e3lg.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfScHPCRA9TVsSAnZWagAAeCkQAIjZovUOPH26kRqn+Rbd\nAFiu4XRdv7I3pHdgWUjEx4IJxb4bXxDCMDKmjMXkBsuOrfVd9vyYHUhJumNy\nuP6LlBV1tRVul8qscJpSdhF5MKwN9QVk6L2jsYF+VNk3bhRKSSkSWAwZnJA9\ngTOpjlcftxnGPaXRGVrC3hQrmGcdYrbkzTJirGvxThxLQA4BtM6Y3KamYGIB\ndosv4Qa8GZKDfC5y6HUiqLXor7sCTtr/TZzqxmEeVVHODqJNK/k7K1Yq3tZ2\nAAhFMctSOPzHQZ9EbZO3kQgnlA+DG1j0FVl/O5LV/Dol05VkEtJr3jQYPiqX\nfhu5QODsFhT5lItXD9FMPb3tMMCyzZKCvlznQIAfHmIGSjL/gXV1wcd2fBEl\ndF3hFtkReJhcFxNbAOwzAzaDuGtUfZw7aa9jyZPVpqotM8EpB4K08miAu4aP\nK/3lSbDZv0faT338BkkYQF75hxzewuW/aMdCcTFhsQszlHzl18Uc7EB4eDCk\nPSlWS/z8clBCuHuoXmnuLPnZowI5UMWIMlV7MgeSntYRMrha07sKiYfziKgU\nwFpAhswsrBX6xTanZwbOefs++Nv9tE3mE1xddBgFreYs59YTE8Dmp+8jpeDx\ni0h+LLiUmUBGx2YVRXvdLxQdfNt+O2qpFm1gBfhxYXRbIC84sI3PLKFI4OA0\nzlpi\r\n=rqSz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDzX2VzdKHsplvZXi1wBVP/ADCva1nfyDuCm++5XWpgmwIgTLY6h8YOKe8HaUXz787aXJQmwFeNayNt1MCU3oDT+UY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kef2e3lg.0_1598669263472_0.5075969153774507"},"_hasShrinkwrap":false},"1.0.0-canary.kef5312a.0":{"name":"plumier","version":"1.0.0-canary.kef5312a.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kef5312a.0+fe66d98","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fe66d981d48900752b10ee5d052ad62b37a455af","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kef5312a.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-HUj3/yj8cCuUWA4p+iv4Y1RMI08ieFf3iMpepSpE3P40KqQmmSsHdfZn8N+/eXkTHAiF8mf0kDODO3MR+vMegA==","shasum":"35d762150a1722edf70a366a4f0f17743d7c4388","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kef5312a.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfSdN6CRA9TVsSAnZWagAALN4P/3EJuZ1kx5VpwRI0kzBd\nPP/050LSpPcEbn2Rc68fAeNFN6oEOuYwU2IvTsJ18Jl/Q8/4VL5fPpgedP19\n/Am2sq8bU3lohexpEUGg/CjoIfo/ENrWbOxVLPzJlFqGtRXJGvJquvuBaTyB\n5opVlGz5EY9YBMq0xsdCfIwWX7InZeHH2rnkZFw6wZndGhPXIyX/c4TwCLDe\nJlRAWqfg3zR2MduYFj33Ic0nTkius+CK0PL7MEj40YzWt4rZFXZupEGE2Mct\nHkG3943WaSpzHlsJKV0crbrU3IA+ELxY7D1c5fqQMBbI5JJ7Rud4WvCN27az\nLxVluhHXGigoeNyOxNf539hVYAXjC/hajWbKQKupHDUCbA92Lv5RPrru+lEs\nRIHujnbRVI6mrUMMqwkFQj/81/aIc7QNXqAaAtc/d4nmI+qqQ2HEKPMoDgQH\nSwxOkNBqOCMkfEzMm+2bWYyJVKQnvRqu/jduXPc0j0z8OFG66qAvWqEoFR6K\nFGO1IJBB2m220X7Vgy26adSfDQtNV04xPn2t1AA2rFluLODoBILhBurGv2EF\nxTTtkCp9TYOnWi1R3eqNplvLi7l2a3RSniO7ypNqbIF9IdRkoQ9UNwW2ulHB\nL6c8sIFeaWw/ZdncQwhgMmo9xymuuNQmjWT/aZqLfBTDsNJIFN0BfFT+bY5X\nlX1P\r\n=7AMr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBZQvR4zpcy/kpzjf9RVIJGuZ7Vfym8281scIf7LjMwsAiAG7/Q7b5LMdZkm3vcRXNH0GpCbx8k4iwKhX4NLD3qmrw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kef5312a.0_1598673786089_0.9250974263687883"},"_hasShrinkwrap":false},"1.0.0-canary.kefh7qik.0":{"name":"plumier","version":"1.0.0-canary.kefh7qik.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kefh7qik.0+a769d42","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a769d424261128adfe9a66360104b83e9dd44277","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kefh7qik.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-x+ACP7b5uVsD7t+apjb+CXZ1TrVSgAzOYDp7Ygyeyx/APTv8KYx8x/X9aBzC0/rWLdeLj9y3Arxoind18wheNg==","shasum":"ca813bcd6a4d84c5eeec6e39f4bded7f8f9a7347","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kefh7qik.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfSiMSCRA9TVsSAnZWagAA/P0P/0t7brH9LC0MIuIh0AcQ\nX5mm3oUECDGbj7Fny3ZiKIUf8wjmM7Rl43Pz03E3zkWLEn6e3yluI7tUymVF\nD+cLbnXubHkDuvSH7Gu47S383QTKfnSdX+QbuiJgzFq+ddaWRdrXSqcLypU+\ntE4Jnm+Y9GwalpGnQFI0sg46nBB9zkdURhHqEKgKEttUzCmR7rpoUtBtOjxR\nJJ1P+Xpptc+Y49P34SX90b+bH7TR1ub3xuerNlw4ZC8RRlrHXJoWbq+kJvpn\njxxl3F3mvEv2ACoY5UQPlDTAJyQrHmhHGyXHCe7zigtnCNbJ0giZu5T9jg6c\nnsUMfqjnpedyvCfGp1/gSB6CZrNuIiBeqU/D1I28fbCHMp+QKzY6fhPCfR7L\nFR9kJpqbzBRJjwqtnnxZo2YElmjz/NnwcuOsXUCa+TXXrJ29zciK9SBbJpDS\nTGLyE9FLlq3IRUQKB131HzmSO9XNwSWc+W8vhs64ULKgmG6BNe+PyRRVS7jC\nm5iXd0iOh/65yvtDVdMK9Y4V3lb/IX2Wsdijv4EnI41PXYzCVAry4th5c9if\nFaXYfYcipDf7N879WcZw7y/C/hRvT+OXoxbBw9OrvzTJ0oXLNmlGoLQV7nOd\n+xx2IUUsKJ/7Jbvmu+0DMsT/iqSRwpO1TjYEb6da7b1L1zaAO88FF0SKlMgB\nwWkl\r\n=4RqZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFOg4xj9qL9cXZ0m0A1uzHgokNTJY4pjJFWv11B+y6p5AiBxVl5uOsz+t/iRa7IdNMOGZvEppdDH2Eo+AkhKfD08gw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kefh7qik.0_1598694161755_0.49101845907247044"},"_hasShrinkwrap":false},"1.0.0-canary.kejh0j48.0":{"name":"plumier","version":"1.0.0-canary.kejh0j48.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kejh0j48.0+b8fcf00","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b8fcf00deb21acb8713074efb1b84414d992a97f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kejh0j48.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-wkk75HQaInvkyhqlEceTIe0bAeqEN3w8qy778cAt73rorBnmN8S66+J1QpaTuPctyKQBlllf9T/K5vbHhoea2g==","shasum":"633a3e0e8e1f198bb588f32e2be6aa490e87e46e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kejh0j48.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfTdKICRA9TVsSAnZWagAA630P/iYK/pgJque5NhkHOGWg\nJio2TWsrwIVWI2FQXkwmLV7nKoKvhsN8Rz1Vp8ExA6KBc/Q5FNQgZK3jiV+a\nkLR3GPaV4RwrA/MXXhJzbLBu7Tld2Gu1nISIbARiyybWpn+YjXT5Ff3WjF1N\nxNCtHAPpnWhPZWWFVOkbh69H2DLTyOg+z78cnJ4uyayhkw4JvVvjHLgnRRNk\nLjZzghBqTRnqWK1TOk4Vg/FmaihU1POYAZ3HU9AfTOmhY3dyWwfqr882Ddqd\ntp5SqFryh1Vm27x83tFzeLZU3WlS1m8ZG0wJgkN4waNYd0zD0/KK+XjAgqxq\nRkIfhETZXX+jm2IglSm6GOTf2GR/Hn0Rlr6qyuo1yQDuAUGLfl7OfstnIAQX\nGteuo8736RpfzP4O7Ff/PztOpQ0XMvUffiLPzwbS2xbPSS99paLxav32NFVG\ndN5qLuRIwevNGF/nglvRZg2OLoFAWmWkGCH5uQ4KlT+qmjtweiuFWmZyorJ9\nJHdJPDmdAxq6B4Jc16EyGhdcUTJnLg4zDYcAofLpGfZUoJzvlgpFIZ55EDYW\noQvxMqVN3GKkmIo3jSas5M6pxZtsNCHDXSmhJderWI/lpV2UlHBXfrwRi8is\neuzcwT1M3JDQjHpSyTAO6qHxRJAFpVUzoJggTEO61F9hKw1uWV6/yaQLNT8m\nUx5h\r\n=enmv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCw77YCsFTUQT69KaBrvYpK2/6yIT5w98fqDbGhYobV5QIhAKWnN0aodmf3XmtBZanUQ8Hh2q7h+smds/ML/zDV85Fr"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kejh0j48.0_1598935688220_0.9041090397078797"},"_hasShrinkwrap":false},"1.0.0-canary.kel7fsyz.0":{"name":"plumier","version":"1.0.0-canary.kel7fsyz.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kel7fsyz.0+0c1ba79","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0c1ba796324d9de0fac8b8a9ae055fde7308aa74","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kel7fsyz.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-/CH6YlFDR0Hd95TCs1V2HPwWBCDTZdL3mqmMol9/rQ3GONycy+21erZ2DVV6X+xDDcvWThfLQil5S8owlFyawA==","shasum":"be142e9f552a9572feb30f42db8f7f703f30770b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kel7fsyz.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfT2waCRA9TVsSAnZWagAA+boP/i6oMLRpO83uK10kQw64\n3ATDTt5bPl4mofQi4A0sHVSr/WkbJDbrUEiMDOeq0Edqpk09oH5L3esuJ4p2\nV7x/tywxYwEoPKZoSIYFxtSuTCaCBMBRvOrPn1nYpI6aNWueysI30eBmGygh\nPXs3LyVq9tC/0rfko8ufu+tP6gFlIHtO7lEgy9+etRrmCFOaB5F+jnwzdDDG\ntPq3Y2etfHj+7mP3pCmRJxZN9pjKs4luH2amddthrNWUj22kMPnO68NPk539\n6feza2wHDhs5jeoH54StrCWoteNDawrUG9JevjQqucZqkdNNkcEqJXrVVtPe\n349ynY9H+dnkwbPveNtf2OfY+Aw9UgQ3h/0Wwl5Pe+NUC9oSi1PKlICAWLLF\nanJr+wTqJe3CN/BdV2DkLTHvWuwbIzki3lDwjySxhpuzlfgn8W+/iczwXx+3\nHGmOS9mhJK7e53SWPyE0Q0chiO6nmnRNugM+NPJCwJFToiUF39cny0AfxP1z\nRgjkPOvUqnhbYYOyjzKphwoJ+oVpGNE65x3VJgAFUaiaVRQQyEtuAZHw0jRN\n7twoQZPHST3QZFa1uPrgXcEVq2qlk1vlnF/oYXlcmSajw9calZXr+9ViOF8j\nt6xZwQN3bz0onrMMA8sLnWhPSOhKIFuwcrbwW5QwMfIGRd5Pm7NPDEX8eCeD\njTWz\r\n=v79J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICxmzhnNeLdQXDxMN/liCh7EjUbBqNL1E98VelMVWeK1AiEAxLeXYfofvxTTyIUF+Xgdcckyfp4S93Txh9H3Ruijctw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kel7fsyz.0_1599040538003_0.3125580699389061"},"_hasShrinkwrap":false},"1.0.0-canary.kela2rem.0":{"name":"plumier","version":"1.0.0-canary.kela2rem.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kela2rem.0+6fbee48","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6fbee487ad1bc9a90a297ea4049ead025aca6a21","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kela2rem.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-DdAnGjTCOmUhb73WoVSw9yN5e/ujPjjYSEb47LHNFShty57vgSwntO3Agp2DVZo2LLy4g+fx2bv5DCRRmuU6gA==","shasum":"90f7ce13795f1860fbb79be787be4cb2e3c622cd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kela2rem.0.tgz","fileCount":13,"unpackedSize":31006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfT31rCRA9TVsSAnZWagAAvGQQAKTB2HZngy0wSR+omdG7\n9MKKSqIf3VoKwn7v5zR6U/adWmJ+3sWugth23XBUIsbOZG0wurz1GHgiqUHV\nbbiOxaipBBc0D8PzR8J5JoJjih+EhXBd5DeRpAc7+GXnxSFDledoQkx6RXei\n14vsK0e5lsffD03+HJ8S6BNhcVueWIwWT38sU3eD5UVB9nBo2h0/PpCdiAHd\nr7KBJ/Ps8yHtIjpUHWeQbe/WWDc3Mb9xWBoYTTSqiIQSLaHDxfjDKXAbzxdv\niQwoQ0S7tHP/nNHphuCMYQUx7NDdgCkFCgFhUWGm+yidPLU4LnKTlisfu1RI\ns76ijgWrSjQkL8FnpyJOiK/74XX+32Lkt3fqiSc4JTxSNiEHrV/J2luGJ+Ym\niMrHIS8FpCb/4IbHkHQ/z28D1R25ZLBYwpDyP2Jq0r0E/VcM6/HWRY54usHT\nt3zyfmU3nHU2ntCbFU6dKqXCk+v2h2Wa6Xf17CUe3mcmYfKGd2V8TG3eeL88\niGHPMdeBMWDUptVMRy0GUr3H8MgMpVoakGRySJVA124xc1gppEBl/bEYFohE\naHfhF+WA/ynq4V0Pe6b6g74bMASdJa81sDDLVBhSMrMEauLjKqbQNfsqfWNT\n4Ev9NIiBF4RznRceWAqYeISJHXYWOeMjyp3Ao0o9wU89s8PByOBkPJhCFBSY\n9cGj\r\n=io1L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBHBhADaPRAk8uDR4U9NtaNEjyF1DJLulQmGsmpG1uz8AiEA1YZBYAVbRwHon3pgK+Hp52eZusYYSJt+wykHgmPtvYI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kela2rem.0_1599044970513_0.925703466762019"},"_hasShrinkwrap":false},"1.0.0-canary.kenou8nl.0":{"name":"plumier","version":"1.0.0-canary.kenou8nl.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kenou8nl.0+aa90073","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"aa90073420085a8f055370aeb09815c6083d8b86","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kenou8nl.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-BVa7DYENmVifiplQS8RQrH8rbxYEoHVa4wKyYrv+LL4VuxnX1sYcOaknljHjMySnb5Qsnh4V2f7lIJSYHvoD7A==","shasum":"122f60657cf942ae2aee1306141a2f4e150528db","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kenou8nl.0.tgz","fileCount":13,"unpackedSize":31500,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUbarCRA9TVsSAnZWagAAw4wQAKQup23JR9CkqIBElojK\n9m5qufZnYLJlJHPIkYT/eLFjICFwRdyHQNhPJLRNrdWXaicd1Q7Mw8uUhQZt\nS+lz00dD9hd8pB6SfvY4Si8NI+hAsB9fz0TFWg70a2A8P0ZAyxaZc+h0EN5M\nq4j8z70MUF7PK1rK8phVZBzFAFd0h6vpy7i2+DcnGFk9y7f3puDw2JwFscJH\n77CJe+BhhRHkizPup/3v9CVKYz+y9v/Fl3MgCsni/3IEyhYk1B3fVvpEGi+s\nKMCMXEArBefl2WOLwdzIJwp0bmihv1YSGRqelwfK0lz+RTTMUwJflPUZlRRO\n2/VKLcAmNzc9Lfg3+RvrfPB/Dq7l0aWJZGfvo0jXDO7t+L8739Es8jrS8/eV\nzcE3Y6mC3yYg709LZxc6fIW27d8hSeiO3h+NyOuXfJo6hyV5dHcTiDZANKmp\nHQRNM2zXsQFjMVg2CGCbbKcKf6FIP0mTB+00/Yl9nomM+yifNvHh5pf9zg9j\neERiJHepDb7rCkvHVXsWdh656nS7FZCvyvuxG3piMPf9/1j76sGDu971O49J\nOX+riIHBTh1MxxuKATyU3JraZ3GRgKjRt4nbAyEk2Di22JO/rNKqzKq5rrCP\nk5loo48I1xpVFCnwf8xoOMunPSCqOm32IEMDdp3yhTabsKuQfDp+aWZ6Dr0z\n57k0\r\n=/c2p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2R9laBj8W3K14W3QMmWjPVYZ4bg0fzgZSKBcquQ4JSgIgBhq1ERoTzIWz9xUQFcD8aMl1pSoV3basvhiyZ+qpwOQ="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kenou8nl.0_1599190698729_0.08306481427140144"},"_hasShrinkwrap":false},"1.0.0-canary.keoswy6l.0":{"name":"plumier","version":"1.0.0-canary.keoswy6l.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.keoswy6l.0+99f1de1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"99f1de1bc01750c4470f356ad5994ba5baddb6ff","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.keoswy6l.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-AVG8vnlYJlfRqp16DBmIauhg+JaAIUAXWquR94PIMAuOgOUMrfoBQzmrD3fVRBYBOUC2VSk7L3WLBbo+a8IRfw==","shasum":"aa3368e1d0a5dc3c5a2f5ae515ba29fc0bb77561","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.keoswy6l.0.tgz","fileCount":13,"unpackedSize":32794,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUr2YCRA9TVsSAnZWagAAcCIP/RJLoVb778vDUmZ/jS+P\ndWIUlgzd33QcEJeqnxhSK8gSu66IoOxg8uJh4+40DL1Cmf3bj96KaIz63A8n\ngqQoBhFPcxJCMoLy0/YF1M9uJKFg/1xhsyKTxuKLCLq4jzY19N+pTrONmLuT\nKSdZP/aHN0Gb5eV2polZhr0yDnFfNhlmcXEAiZx/bWyZMMjP324QX++c/xKi\nFq5bD7cbWY2xC41juWVrmdsT4bMPSAoJooieZ+nypjS3aYFUHYyGDEW/at0N\n8GbAkVHFJcZtnkk6e0Xhh1g+IjZqCoBSbZQwyS6Qdd1Tw/KlGvklKJocj5Qr\n5NRBKZK+SUjUAkLa40y86S9oKYr9R6s+p3UKLa76du1dCCEp5BaVLYcu5s5e\ne5ceujvaKPFW9NWPWMeCbdhmLsQSOgW2YpJ1o9ptM/qPLc5J0tL1EHx+xOe4\nhaM8gT2dTfuiVv/HVJp1fTWo4Z2yfvVjYxQEqiDSt+HwzkZYRNqlz7hP7KVZ\nCrAnncgMGy9/a648JYUUwnuacITB1iotiBiFKiFWSRuLy317ca7rzVeCVLnX\nrNlemLJ1rdBYgXFf8ND+FFEfTwa2/P2zKiZQE37KXAiZ1v87PF6va++oLOBn\nzgtHVWV2yJTBc1YHKz01AwkFWWnZIl6A9Jws8x3AXK0MdJ2c63FBWqTf314M\ntiSu\r\n=SKOL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDThqfSWi7DkmgpCXOfwo34Jwwlwzqk+d1BZHxhujVKzAIhAJZoGGP/Aocu56yV4TPHdfwwHKGtM2JelfWqbbgoZS78"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.keoswy6l.0_1599258007968_0.7116235051171769"},"_hasShrinkwrap":false},"1.0.0-canary.kepjwxp9.0":{"name":"plumier","version":"1.0.0-canary.kepjwxp9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kepjwxp9.0+d3bb046","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d3bb046a67fa3eb0fe43976b1a2272f081293720","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kepjwxp9.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-vXUGYTtYRD4DEjiZLDbUg7W3Ncn1RGKxKE5t0/u7gp9V8EeMcq0gKohXEHO/LOgbKUBIFK8lEbNFF6Md1AZK9g==","shasum":"39b2ea5d7edce1f9224bf3cb6dd3ebd969e08237","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kepjwxp9.0.tgz","fileCount":13,"unpackedSize":32794,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfU268CRA9TVsSAnZWagAAuDMP/0EhwUgbwNzfl67CeWsx\nwayvp3alWDsBOTfZ6r2lwB8ilUTjZbjDnhd2dSayY6o/4TYxAGvoFfgYxJs6\nFp7l0ncBydvJrxaIgTA/O8MuFb1uvKdVmAKMgAyiG4PoMMx7RYRmzf/t27C7\nH5wR7Yov+xi3GhkJkQIjUL+6/x2Bw9JROQP+zMmAwLaR9tnjVoz++V5oiZNH\nVDSI67XDizpS1WFv+qUErqmaAPp3/VeYwiyLCCvdIPAEgNXSlzx4iFFd+TJg\n557cet4TnTWiAo39KqqYJiINofzRbHvECjMUXPBS2QH9V5XDS0/udAaWSASv\ntwUrnIhHP5+fp5yhv07URXSciRYYZLUYq0FvsEcvullolFn6EMI233WCpddD\nL5c6fW1NCDmarew5lBAeSEB2JvzfZh6YlZfGe6y68qkyzhqZv8zjvO/oSIEP\nQip08TYMn7mi/LQoGbHW2r+C8QY2Q7k7C1FZcTxYi+aQmUu75sbSDK29pVUN\nqwh94gWm3RpATZVnfz95eQufSEMoDmpn+TS46yyLSfh4RugYd66I8qYz6zk+\ne1pq8yGqK4sBlQVGms6OpelVMYNCDtMAydNWp6Y/6+6NT6vaXQvNnM8DMsM/\n3i7ZiDG4LwM7VIcXOe1v/KKpCrIzQMMSDlyj3ag5Yv231HqjDSPpquq+IRs5\nsOBO\r\n=AGt/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEk/iuEMuRVqo0kKgNcV1d1NS8RjGTrbL3AsOI0q9ZmAAiEA+sP1Mf+p88tIfP8AO327pbIXu6/wCM5r8D1zev4+yfM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kepjwxp9.0_1599303356437_0.5514062424644808"},"_hasShrinkwrap":false},"1.0.0-canary.keqn8ee3.0":{"name":"plumier","version":"1.0.0-canary.keqn8ee3.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.keqn8ee3.0+5749a77","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5749a773d86fa866817a79390cc5e6972151a97a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.keqn8ee3.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-c/BuO8Y8xPA3fiKw/9/CWRRo3OVoaE0dUPnPwGU/ChrmJjj+LQ7E53AAxeQWaAvTdF0FDvWA2Rk/Of2JgO8zrw==","shasum":"762caa0d3e60a74d989563f246585a8dfb8491e2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.keqn8ee3.0.tgz","fileCount":13,"unpackedSize":32794,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfVHC0CRA9TVsSAnZWagAA/1oP/A7f9uBbwddLfHFD8OBT\nWLuoAIFWKqgJLiBNbUrMTuIio4EY46HvegK84SS7V7ViJIpF2RSwcSQuSpre\nh4BUIaPCVqg3GbuPUAfFWhp4xt0LYpVU7PGGB+1dfSwr2Ancd1JbTjvZMlEB\nf1iAh5Vg9TSiu9IrY1WfOTq6IuV1Ser7mWPSxaUg/8kTYO4FtSuDHDHlNuK5\nAl6Gfec9yXvGB0f65YYC85BVPcR8F/vr9kFACfcmA54YDxQUy1dmVHW3Lbvu\nDVaruI223WqPeFAN83U5sYVE+tm69NsxGx2rxeCKoParA6OhfcokRVMVLPzw\nrNu8a3O8V0As4hNKUdy7COY6r1+zgsIYyosDby7rdypecsaVc4OlTJGHRpDz\nQg2gsfY1G86yZnfoNItJMTDxw68wAjcbsMrLPrP+HowtrFZXhkGkhY/8cYU7\nu92M9JqiExL4pM3CU3ZoOldSdopPPSH4C3K1TfIYo7UME3+rmyJp6A5yglfH\nt//mGJs7YOiJB66p7hq0F/8L9Jr7oePti74qrS3T+ys4iWxsrpbDVY8YdtRM\n7VhYN1rFwDoMFjYF/IRkpqCbsKWKRFV4tksqEzs4Xwi+pTiXaVcA1GUxXRfV\nk9RBbspflcpqpPpjFapvRQi9od7US5SgtDeuhQcmNiug1P/tzoM8Z5jf2r+H\n4CY2\r\n=q+E0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFzWLBLOIFX8bbweTaGKQikg8FXqh17w5Y5B5WIRNtwvAiAKrDSaX8qa2SABopVhbhKZAlUS152bMvH5+5RypAoF5A=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.keqn8ee3.0_1599369396298_0.9148174115407728"},"_hasShrinkwrap":false},"1.0.0-canary.kerx6yap.0":{"name":"plumier","version":"1.0.0-canary.kerx6yap.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kerx6yap.0+dec0c5a","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"dec0c5a7ad06cd6f42cefb6799bacbed4cf97095","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kerx6yap.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-BURnroAi/XWAfy97W07/K7Uv+uMpsQvcEv9Kjtpw1l3x4tdhj4jWLmqjJy9KLwXJnxm1UWTQ8WZ8iOXFth1tXA==","shasum":"d620c95c3749283b1807f2616d0427e3e43cb1e5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kerx6yap.0.tgz","fileCount":13,"unpackedSize":32794,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfVZ5ACRA9TVsSAnZWagAAmd8P/259N6tijjziSs4OhFWZ\n5YhNUL6mbffKMy6MSbc5sXYw1XCypcCvbiD03Ipnu0DDocged+Yb5z0Ri8Nd\nvgecQMhXc27NqlWqUIfgDsykQ1t5Oe8mQyJLDKqR3dnnXDkcg7D15auT+8uE\nr+Xp63JR402zfzeCBMd2lw8mKeA7OJUJzJZT2onB1C0yBgVthYhEvwjzqAVr\nU7Z4ifRQ7lPnQkNWrecZBFw8BnInhCcDTn0H+SXYN7vNOyc74vlhcW5H6Z5C\nsQRuo+onbU+zNuIoiwIAL1K+qfbIyHhu2zAYkGjlMVXm5ozVVu7XppFm7MpB\ndzMTV+l0ptrxz2fPZj1b9fzqNJiGFEDy12U9X2QGatahJJbaoA86uOkgIOEZ\njV1Y83eH7ftB7v3/NQI90KExBc2/DcwHKRbaJKMD85g2JMCmNRv7by7K7bze\njUYAOniBk+Z+1u+Cj80fO/QYa06l20gOhEnJeMk65VU7UkoHcuCI9Da3uOoh\ntGbxH64CAIjnglrvTQ0kzAUTp8xDGpM4JPZ5xagQp48Ixe74RuqDlZECe8QR\nAhUr/AGJolDtXAlvtHSmz2Yq+9CK4MwgqKr2tvSqyo46EI3bkGiXAbTHN9P0\noDug4PY3Ib/0N5G6BtUgR1+mxXcdDCydTk12WOEqrF23KXw7TO/B2YFVR6pY\nmpPF\r\n=vTzr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIExAmT0I/h6PqSc5oj/fqVbw4cEGAGIPm3Lk0pZrWVHLAiEA1jUzzFVnpT74EfuGvRH09KOo/SdVJy4nZNz12rvHPhI="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kerx6yap.0_1599446591875_0.41476450839706125"},"_hasShrinkwrap":false},"1.0.0-canary.key2i6eu.0":{"name":"plumier","version":"1.0.0-canary.key2i6eu.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.key2i6eu.0+81359dd","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"81359dd00f41443a1772929ce7b73e9ff85bae9a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.key2i6eu.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-IsfVjncew6kdKdphNJZ7d8c7pRcxWCstKTdoTClXFgks5cd3AlFYA3dAJO8xJJSiUyjOcE3Zjhom1nUye5JVfw==","shasum":"3256f18b2a530703a65263daff449594617e789e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.key2i6eu.0.tgz","fileCount":13,"unpackedSize":32794,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfW0pOCRA9TVsSAnZWagAA7TwQAI9wzalLyl32iE/kNuCl\n1adpnvPL+4AVGT4a+VGcjFN6viM659oo2VVKFzH7/sWTKwUutD4HKehPYVUb\npOpVBOmHxgyN1/DK0N7LHlaaXz93suD1J0vaOilXRqzs0dWJxXLPGQvS6hYf\ntjrX037WhawflOtQL9IfpNyVEQ9WnHcNzZoGoOknMb6AggMDhgPvyTzN8uHw\nJXOkHYsPkPcwqLiMUWtrMf8OxX11QaAC9+F0eUEMTXcrbHZgH1Rk105KF3X6\nGbFsZ+ao9wo/hDTHPLMEBU92MEbUZytRJ/G+mMd8y38l2qic9DsgjdT0m8Fc\nbatKMSc6fVKROJgxUxsJaO3HwWRII7usTBlaufbkUplxsch5qCpNLJPqHtaO\njBI/pHbXJXKhhHKC0hsWVffHhtRX3mgZo/7wlSIcdEn9rfphzKpjAHFTX0o1\nutfpX20B4qyXqVCeVGZelfaGnx83Yw4eh6aPmTV82nBQQfZL/U2AlReXwEUQ\nLSNHTACBFNwqKJN+KEOrbbZg0vRvugwu/zM4LoIiv45iEP/kr9XYnXhGM4+H\nA2TTj11VwiU37U4xnrMniqwcLJyuRhJLAQZlPUVfaRLBwnKzfpHQTqgLY7IM\nvf3iPmhq1Q+kgdcLhnts8C0u9NOfAoeuPUqHnlRhuu5c7FZRH62DI7suXpwI\nvZew\r\n=0PUs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID6bpMKt7sNLhZK6Igtj9+eKNrQnUi/X0DiR+D3TM+zIAiEAu0ifOSng9fo5qxqQ+/VdsHWv2CzDBF/Xu3szjYhXhf0="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.key2i6eu.0_1599818317867_0.4093428439740887"},"_hasShrinkwrap":false},"1.0.0-canary.keyz518l.0":{"name":"plumier","version":"1.0.0-canary.keyz518l.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.keyz518l.0+a6712d2","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a6712d2ff49cd1ce4893ae2e2e72ce386f4d7af0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.keyz518l.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-9Wii2nlSrIhoTk8gaipQnegcaYqHkQvGqL0+6n0Vv4ULC5dWdXGBJaF30eFxURD95uxAso0TF4gMYmLipTCElw==","shasum":"bdf3b0de4d7ca5d22ba5fc0508a9cdfb334e0117","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.keyz518l.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfXCBoCRA9TVsSAnZWagAArRQP/jZmSUTtDRwwAtPM1SZh\nNOd8qG6C0IbNjfNO7vOvsYMGQmcbC8tIXHxmVlupDYkYzPnQVF2+RS0QS0oX\nV3CZiGFN0ZQOLVn4pJrNLzwuptSipBAgNaqFUYYZbkpwGRIFXuHcWnhybkvN\nfdtQ0yd0tdN6kAsnHrT+zFKtet8WP8fIiKeJOo+jxQImaqHLv+rPiXvDRMXs\nDPgvbtU7W9Xs/qNdq4SUc3hrtC2dEjCZwdsevBVqa6KMC/VWjL1Gg937BfF3\noGw6/P3aactETnbCtnrpE6fSeM3QD4z0L1KjhfeSAXr3dQdu7Frdmg80C3ao\nLoZDSARi/cvEebmGi6Vo2w8V7aPquBis2OveEH1a7QSYisMQu7aT59+R9H8q\n7ul4Z4PzLT9rBpSSyI8xiSttK2GCOObRjf+22BiwJVafZFmPp8lPeCPLjBhn\nY73vVx11sziySBy0IV2uBJ39U/IAG1bwT+/ephL8NUdSnB604tLhttLyW5bh\nscQmNTFuCLWWD4P0JhXaVHFRQuHoL2O/Z1vi7TgtfT3I6LRHcSkS1vySjUhP\nuBQum47eiSkjsWTOTopcOJP1suCbqNyVPw6AY7SGKEPVZfyVOhtMK7UJ1Nt/\nz7xOChkvPydnCd8c/uBaJtExFV6goet3+ehgeLB79KxTzCpp84QnbxU10f5i\nr+xW\r\n=8YxU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDAqAMD5wZbG98HhbyprC8tQL6KJj4nlOrAOpvhQS2VowIhAO91fSWgVjdx7Y4AIIrdw0QWYRYF8f2hlnPhXnaX4BXr"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.keyz518l.0_1599873127721_0.7131234441812182"},"_hasShrinkwrap":false},"1.0.0-canary.kezbpkd4.0":{"name":"plumier","version":"1.0.0-canary.kezbpkd4.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kezbpkd4.0+e02e6c6","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e02e6c64767a66656edd8fb13d9a687a9f757a0f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kezbpkd4.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-2Rsh2/hpF5bAQZ+BbTzDzL4jOh0QEpei1dkUhlgWAtHm65MAZQZiLpDDxQef88hs6o/MFatWfjFN09MrogCs6w==","shasum":"8ae580f809ba9963ef92ec5f8547e23982a7def0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kezbpkd4.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfXHLeCRA9TVsSAnZWagAAA8IQAJxrlI8u0jsgN0CTcPvU\n5hrN/ydOM9ClvU3BReEOkEP67+Q3aJYgk+TFqRKjUx6BVAHAz9OqoZLRc4Td\ndEOs2VVo3nzwkG3ioN0wOJ17To1H5s01qu5RxlnJkGZL32Na2JAUOFxXPpwZ\n13WZ/HIJ05J2252DVAJwFF5xXT85DarYhOiQdRGKqvCdzJ3mZ4WpSIrgsuam\nCa2nkJubGvgs4oWyTSBI6Zlr3wHT2O2u7SzqxoHdIkczeXtfdZw7FPWAXcEL\nRN04+hFEuUXSzx/xxDAmLN4TqSYYYydmsHLNVziA4TyuFwj62cOhxhdTVnQe\n2P+NtY6Gdp34h0Mn0iZO6RG5UXDnglg6vmNTmhflqjP4Ltw4JvoBiGMYiH+j\n8qpJITBlM+S59GaSxLr8jFkRUEk961nSo+CmJ/fu9tzNhUU4Q7DWATakZlNx\n/mM6ZiLg2O4CrRdI2wE9bslBpIqlCVpxdIl+rNWlnXEbPDPNP8sbZipk6pEX\np0mcujrKuLHA+OVpLcNbUpzGBLaaERPFiXxvKr5vCuV7F8jdTSghrh7neA/x\niOuw/kijU3vZTJ20WC4p2nwb/24EtUCZroThy8lGk/+8vVka/3izEdQYYcVq\nbOEdlhdQcN6O3Oropl5ObiMHpCR8MhfIEl6QFl225aFo+5a/JKFdduavfa69\nX8jm\r\n=vN9o\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC0G4KYT7w2G9zGQie5akDtHj+FgaRziovF7vrOzi5aowIgNH76oRtlfqtTdxWV9NQO+44ofYl0Wq/oY27WGpTNXRg="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kezbpkd4.0_1599894237768_0.5817380151390179"},"_hasShrinkwrap":false},"1.0.0-canary.kezj7wpq.0":{"name":"plumier","version":"1.0.0-canary.kezj7wpq.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kezj7wpq.0+23eb464","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"23eb46463fa91516e3dc94383ad1076d56dc6cd5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kezj7wpq.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-jaQg0amWVi/eaLaFJQ5fBdHwOFjbsY1wqmxkr1o6RTR3XlnpRo7SJdmI2Febb+ggm4yqJda3OROhuo4U0CF4lg==","shasum":"0b87c84d1c13f18402118587af53d34817170e0a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kezj7wpq.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfXKQlCRA9TVsSAnZWagAAKdoQAKKmDwTKV7hSoLLBiY1P\nA3oOZMOjzmGhOGy8TQNIPlo/I3YF5Ib5hq8kdAxR3utvgo9WDhFRyx0dl1kK\nhbBPNR7v/2iW8qIuxa9jxb76yH2JSM0Y7+qiAZtYKujHxAaPr2SyGZt9v5BR\n5T8th/xx+/V9e0aXGR37PVkOEKGv36jNGjt2JvXN822L8Q9SBAv6703EnZ+p\nK5kGT3opbVfHnunGUgLIlvarcL+oYBKRRqFqOOaqgcE5Ct/g+jZgJeicFwN/\nXhytO+HUI5VdbDkmB2jGu9PZTKpakP18Ob9Vf8ueAFaWXFgRScH6oPqeSwKi\ntWxG30PLBQ7uWdVGyItOxlYCldtjC3y3n8Zp4dreR3GiysGZ0q/mxJa+qo5r\nYvepPfh1NL4FeVLAG0cs+yy8eUDj8WNadpPAVk+QBXmEr+OgvoHCqzjQI6cA\nZeTQ5P+VLVkH/PX3He4ztqFvMdkkcIo5q9guNg8mHP8cKtEusezkvOVd1OUR\nKkqbdtpVYHE104B/5PdOobOKYYFsyRRUHdiVD2QY5JZWIFq5GMcDoyZsKET2\nYQVxlmt4F3uQ7JxO4q8iN/sOoBstLl5aNt9CKtbPxHCNiQGDYg3qWdkf7Z7l\nAVgRFnQFm96tYvM9O9FtYS5GPNMr+cGY78WqsXWexCvx3iKeJspETaa2ZBtb\njb0X\r\n=ZUj8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBFkZ9DaEcl2JK6V84ofoXWIX8lDETqQqU7KBoieGmfIAiBCy6eQfhZkVL0kKaWNqloXDNL/BKxjjMRSSZ0nwuyQlA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kezj7wpq.0_1599906853167_0.7403075123389515"},"_hasShrinkwrap":false},"1.0.0-canary.kf1ngfd8.0":{"name":"plumier","version":"1.0.0-canary.kf1ngfd8.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf1ngfd8.0+27fbaeb","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"27fbaeb8471fb1ea8009bb02a9248cd13416f58d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf1ngfd8.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-bDcfoR7vJiM7oQ1n+vZvCNDEhnaXkx4rVYesMSdCNkPQapVHNk/UmuOfDUBbhz9lDvsNy0iZwQnkQrKvMObEDg==","shasum":"d11e0675884870c1a65d45b1a2d7286a7a43d9e9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf1ngfd8.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfXphXCRA9TVsSAnZWagAArGAQAKSFkbKNOaP1pMazMf9b\nAD9MwQOorAUnXqaw0VB8L3eP9WAbtAbllD4BkSqYQnXsVPbNRu54lVQ/0ZO2\n40mlZfjN1vgkr+5GDfEnBIvdcolG2P8ugYbjYGgxf9Cq/v0LfZ3Mqdxq7Dh1\nHlFrx0aRxRlIavIBjjGL+2yB0WlZCGX3cjyq1gMqI3LiBff99YS0MHsnCqiq\n89DPsLxu1TI74ij2qeCDBjkSyjsLmKG14WnO45Syuz2QXdmoCM9PCXzbZg6u\nATjayMHdIExF5fd7qX5ZWHROGQ+XA0FF/IDGh0o5GXlZjhXF7rA9ZN3tygVR\nKpT6dBzsQ8gVLrYSEiO6vxTtYt2Ezp03Va6P8I7E8kAfHVop4ur9+0T3XigF\nKOgqiFw4vX/LUhMrQ0I7sEesuUd4FJHcjezs4kkdv791jWvTHYPoLD7qSB1l\n30jO174uh1sdpojG5ryNbiIxNlYXb1Nm6e5p0y9BRHhEYIMnvDTI7cPpeKQi\nIH6kouoKa3r99sWKSgly/wXnSY4QhsZFtERs46DyDKxX2lvbDpqQL8tjqvE4\nTfEEXw/jwQTTwjAL80sAY+cB0x2jnQoOEerKdflk69qhtgGGlnsALao0CauY\nfxRRJWoK02kNvSR3YL17zhUQs8WEU7rpUydLKDUND7v3LSCSWTXzRMWGu4dt\ncYZg\r\n=4go2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBpVtyBOEx41vVW0DuiZHFNdSC+krP+RLC7nYL5ZnRutAiBwZwMyY+wV+3HTKfDaNzgDZJhABZkd3PD5cZlbuEtT5w=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf1ngfd8.0_1600034902586_0.053317221025066086"},"_hasShrinkwrap":false},"1.0.0-canary.kf32nfdw.0":{"name":"plumier","version":"1.0.0-canary.kf32nfdw.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf32nfdw.0+cf3025d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cf3025d29ae34818da55dfafd81f4d1290aa8379","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf32nfdw.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-+6/a8hcKsjV6POAZSKDOUKZw8onEwdRvUSc1qffKmBisJrbBAMjCW0CjIuvzZreGFbBWG3p9mJgsAijJ6muIBg==","shasum":"402dbc330f38244ea88e73aab476031dd4c7eda7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf32nfdw.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfX+g4CRA9TVsSAnZWagAANcUP/jO16fIVKWHnT5JH7iZk\n3qpJQeBOojwVx/WtE1VzJC4Q9mPisai/HreF9xqPguQEHm3PKgCmtMiWpZ58\ngzefNtOPfixGZhVFCQCM7NebmcRyrAkNjJQif82YhMnzSRp2kia5LzMSF3z5\nrkAhqpsfOHrRrLHfxnw15akN2nb4KjXkXfsKuB3KXFR/dodP1+z1yraVRnRt\ncfdlXBBYOwP9UmYqVTHwvumYVfrfbnhH+r/y4klyE1iaClfIN/4+EV/HIXVd\nIb6dxli/Q0sF4XrOPzoQGFmwhKX1r7DPBo7vFtICP1SGogFDWo2MX0CJyWki\nqcTfRID8eAKQuojcNortuqw2CUlvVpJ6RLccJ65uBarTbasV4O0n1LDjnAzB\nxM/BQTXz9mzKwnEg6xxifk6H/WfdmyEazrL+auTINy7HVr8kUaV86JetWO+j\nGMf5hykCZgc53vZaFv5Vo1I8vMqmTETZWRC2abFbO+187OM6L8HlpFO3YdA9\nzY1qGATWMqzipxnwHekr+EDPGAoGFCaVXsISXZGDs1ltibHO5XFFE8uIIM0G\niwrR/dea0r+nT+G+HdN31RMn9rj2bzVQdtSrALAFDbMwFhCcP34ATRavf5pj\nEewQEgSU33aIoiRmEv2Ymcfev0eH3ClrzSG2uG7/nKWX0YdFAswaSQhPKASZ\nz4qS\r\n=czd/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBz7lyaCXP4i5unDUxLEZeG5FOYolw6hAdMLu0QNnBJvAiEAhIcjiZY0FiTfEOWBtvhur9+gV4URpyeI4JIsSiKol7k="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf32nfdw.0_1600120888458_0.5260834441910738"},"_hasShrinkwrap":false},"1.0.0-canary.kf3gt31v.0":{"name":"plumier","version":"1.0.0-canary.kf3gt31v.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf3gt31v.0+ec6bede","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ec6bedea41b8078b31f16157ae1d909390979029","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf3gt31v.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-6USFBSrBkDv4gUqKgubie3ZH06NE+2JXepTiYrC+tlXwiY+jE140rzGR+07zRKhKkgWiTV46MUF4pr0Mzn5EcA==","shasum":"0076eea4bc2d257b154eba1be9f6b6608176ce0b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf3gt31v.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfYEUaCRA9TVsSAnZWagAA4wgP/2iJcGu3W6yG/ZgaeXVb\n1WD2a5pN2c2YwzNEkSeFTCfKO0nkP5+xfyGkRW6MCetm4gpqzy/3W42d/wZI\nw9/6R62IxHHMKTCZ5y1TqN22VwQUWR36toCNeAa8GIKyrOcXNadHv/YIBOue\n1vIZ0RATKSX6FtTHX2idoc8xyelR4EMQtA4GWOIwhh6VDPR1lQOXVEMZbdoi\nWy61iV9qJvH9OMkaQWKhQ8MeSae/wqIiBQovdJSGa8DstNogJpPwJiw2/Pub\nFllMSd3E1mSuzWCe+6mNZ0LKdvilZ9qH8AXhYwww0uLluOubiTe/n+8TrWTH\nNDmvlDkbCNOxpsB9B54Q7EA+1MxFbNBEL9RIfFcHUgNXfTfieV5fch6PZY1R\n7fgTgASTXxRIqR9jusRJ8cyLj9Rw4Xqx+iAb4/BlWVxTYT/KHKx3Bv4fmP0n\ne+eA5uDzdMoJiGXgnCskjYVCRBiE07bjLy3FCpSiugMwp+m82uVB5xAXQYDs\njVlZdf32flpUaAzolKCsKFOo1i3Txia4kyLU8sQc1b/fP9iVK4ab6dL5CylB\nbFMne7GuMAGz7jyLEsAfNcHwbnCqz6qvuwti4d2AWuqIuLqyCJt5PHx89N6I\nsrF3kJYLs1aIeD8VNjGuutVUS6j9r0KN2WIgn6rs79aBB+mcl4j4Z9SiFRr8\nnkM1\r\n=fvea\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZF9hIi5uUwqrknE2D8hjXym1HJ/4GNiEuVBIwcDFIBgIhAIZX4wu1zaTZNd6E79Khm7t9D+V3Qk9IyJqU2lMSQllW"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf3gt31v.0_1600144666313_0.007891213376610029"},"_hasShrinkwrap":false},"1.0.0-canary.kf6gpx8m.0":{"name":"plumier","version":"1.0.0-canary.kf6gpx8m.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf6gpx8m.0+9bd4c79","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9bd4c79ae6c47ce2aafc2015d95a1cbb7e4b0ffd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf6gpx8m.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-JivGdPyJFTAFBJIVlwxipnvmmtGvmSiecAPGUMcEsO03RtwympuUhHCU33QbTl3wmYfR6BJdzLnzVHTEB/+f3g==","shasum":"5f453a06bec55514206058fffc83269d7ce861a1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf6gpx8m.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfYwkiCRA9TVsSAnZWagAAqDgP/2fTybLlpRwN+9I52urp\nv/C/ujVk4XILeIR8tbS8IGiMphBIgo+q7ujpgkGKiFPQ7ocQ9hgUWDU0wYOw\n6PzI4yqfHXOqSbKx8ZBDRy5SNfAt/q0LZmlqoI8bGQ90bfVkpGlELth3JhaG\nkF0se2YDn+bxkOVclPK8/YDoQFJ/5EgbvygzSEnrz6HJsEDHzMKOjl+JgyrT\nPJv+pE8JGnTyuZL196aMgumMwtbQW9/8TdGhMIO8x6zp/hahQOYfU9pWZ6vW\nEfVv4TB1RLS6yLJXal7+xMZy6FsM7zuk0RhNjUh3KYyGZt6ecBGx15x398XH\nE5EjiDkW6P+ugj6TNAEyy0ks73c6YVnqeX/d9uNto+53V8Ua6zfzK+Y+4H9u\npYlQdsWOe7QqCezq3C3p+Qp9kZh1S8FqBHdnv6gHl4SwPMh+YTRfDQkMUIu0\nsxE+A2EBJ4TII1sXef9kunIOCvlNhGI6/EkqC+WFXHIE+Ubw9mDQKNmf8NX4\neQchCQFkBoDVspUDvsj0/Fckj2hrGpXDXA1kRbIF1WbRCGF5QkdFK194CJn9\nqGXVlrYjZbeUPCdnZQWeqj8RZBbfQtR2Azf/EgIOC5wzpaElLb2E9NYZtTJZ\ngF/lv/NO/8K8Dzf/olaMPlwouDYruwL4osYsVCHxynDNLLdXpHBrb8VsmFRo\nzvxq\r\n=KfsS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHQuKE53VIJodvblIQ8q84lUpz9TdTaq+S7SNqxHxHMcAiBKAo1q794p9MjNCL61Ckmh+wBaquzHOJRffX57gNf/0Q=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf6gpx8m.0_1600325921625_0.39038373694915407"},"_hasShrinkwrap":false},"1.0.0-canary.kf6lwncy.0":{"name":"plumier","version":"1.0.0-canary.kf6lwncy.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf6lwncy.0+51fb238","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"51fb2384c3abffe1de4104e776e49400c8b287af","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf6lwncy.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-4zbcdRp/+aH3DBs1MpfYD5rL6LYyXQHcItjVrDSu+feGBvWLdJM3wD9inllIrINzuzvqlkAQphCS1JMXGX3yIQ==","shasum":"1a4a9bfc9ee8b63b1bc48b9051a01c03f0ae33a9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf6lwncy.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfYysmCRA9TVsSAnZWagAA3cUP/jWqez0q19D9uy3UwPpy\nTcRdHZQwFSxHslX2dZQ0BOhcPXlAkxnQNIS4Vyhv8Zo0iH4mLStWhF7HvnvZ\ncwTARfczVHrJHkyggZLGHFVRHrDCrZqYcz1pi8plw/zqqjNBzGVci49wCyM6\nnLmTmKslV7cPivfusNHXa0MCRCZRJTf18Fvxwq7eV3fz9f9c2JoEPMd09Wim\nfaPRZO7WguK5ZQSc4BD3do9SNRyppsrfixjRaYLK2VTzeikwKGXBdE6vt6fn\nQDqOvw/1EwzFSGWKX59k+DbzS1mm891CT/iXGm9z1znlNPLw6RFLgC98GLWq\nY+7N3JQEzWriGfdcn7uFGsMre91BK0+2RbHM4/TAeDde+rWK5f4TC1twzOh+\nBRsvubLJbI/h2OTB5Q2Cr5vGRv/GKKuZFo0KjENMh6qMw0oxoYG59vt5htO7\nV7y4Bf0GgKlzxzxluMThDOSIEp48mq/5u7szM6DJ/xItvVpTlompl41eKp1o\nLhueF/tW/bSvqoz6REg5+DNfaP8QykjfCd8rorsBTMwfAb3YtFRmFdrlNByX\n9OMt5s2g/ZIQu1MM0yXtotrwA9/e+ccxRp3ERIa8dGoKR+ZiFbqW/wZs0jKe\ncsF6aX2666NAvR+FJPW4mEcKS/+d24OzqTxHSuvHmWf1UHP5v1oLyevnnzXv\nQZu4\r\n=YiFG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDgMgREQjP1mJl1XpHWJcuILtCOYLZ9oxSugJVD5w6aVwIhAOBM0RSdzXgLFxO3SG4oRHcnD11lG5RTRR8oFA2/7X6E"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf6lwncy.0_1600334630089_0.8010994734251689"},"_hasShrinkwrap":false},"1.0.0-canary.kf6ox8zq.0":{"name":"plumier","version":"1.0.0-canary.kf6ox8zq.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf6ox8zq.0+9230abb","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9230abb52eb0a4000eadabc0160a513ac17f67ab","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf6ox8zq.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-YbYopCvt6EyyGDFKXL+KM1/t8tj6smwWe1a5ro1feYbDzd+ijTKwcp5qQIdNxZAPPCzj+Fuol8jVKnPwTfjQ6g==","shasum":"0198f2f95846961dfc68c58978a23b4541e3fb46","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf6ox8zq.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfYz7vCRA9TVsSAnZWagAAaI8QAIjCFkfbMoxbPa6bi85v\nWulm1B87I4ziYCIIzEUIo0glyz+Lua7SUYZY6rnZ4rQSXVJ71v6fu521sKXI\ngxJot9BJZTfP0Td6rm10YNeHO2UU5+5t86rTZKCidlyd/aQRc/ui0e50nodE\nf4TvS13RojD/18XWaoENOyZkRDSjedJo7APlLwiDTNIX2jtFg2hTLHpS4m+8\nviqFplsbMny/0vwg7Tza1dJaSTC12RDPfUBRtSOshD+uJbUpfdVTsApBLVI/\nzDAdIezb2V25NOdBpTAW3dpq8BxpEKIDoLcU6ktoJIGejqsO+UBN5obe0D9W\n+lu3h1SBrYq1KOHigfAzX6InaNSEZp5KWM8bM+Y6/kwsyRis8P4WsoakJUrL\njDBHBykq+6oC3TsSJgt91fw4FNZEAgY2ZdMRqJRh0QbtsIQ613KlkWqWAALc\n/GgfFQK2mI8AukW6igAwjtrOqxuBooDxET5SFa+GSHBb9YKK5yI04T/6rcjg\nCJ2b8elnVAnlbSMWl0Fqc+DjcIIobXLQ1gV6kT054fGcZRpoL4OrlLw6RtaN\nJoIQ1HhGciDF0eJjeOD/YWuauTioGwnKgCbK++39bxmuVt/URIMODJAISHkX\nphSJ49cXw2YLIcJtsf21yDrzbFevamGFM0PR0azAuABeBk7vT3SAL+6HxK6L\n4FBI\r\n=UkKM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBPgxEQZeNAz+2cUl5bdA2x5ibUlmNorTmI+UZLRswkhAiBPzwJRZqNWI7+OB8+nWUbCtCEkZ0dTQrFuCbad2j1rvw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf6ox8zq.0_1600339695488_0.35503410152223025"},"_hasShrinkwrap":false},"1.0.0-canary.kf83flsl.0":{"name":"plumier","version":"1.0.0-canary.kf83flsl.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf83flsl.0+07c2c89","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"07c2c89cbb0cd55577359e45a3d3aad20eec1827","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf83flsl.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-nmtWKU89lZhtaa+NoZvxu1l4bcDTfiCp2QpYaEGn90W4tu2q09E3Qb7XCP8noJrNgv8mpwBE2NZeofftujvMPQ==","shasum":"0948263d80e2c2ca80ae2cd30886d815c0a79749","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf83flsl.0.tgz","fileCount":13,"unpackedSize":32492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfZIpaCRA9TVsSAnZWagAAeM4P+gNrElbJ3Yi4ttKDwy8S\nVFPwFPfr7J42P/Suz5IvyOcRaMlv2gsf2gF9Wwr9t1wJbXUyDIZ4orhHPBHS\nVv/mcod1NCe3WzOVxN/xVD4qKyQx7h49RxVw86qUTdUnX0HQlF5EKWGPKCjw\nHxQI0JYleSVotro4BJ6HpN1qRVok/7NtIqYGLUfXmkw/BLwl2oE+uviEhR9G\n/RwUstv/JSf/LAJW+8bghwzi9RI0TZyeQI4BMNvh5WcV+YNXorUB09qP0aC0\n1R1MwubbZ6TZIpK7X77fe/UdrA8IN17iN+eRjslKCGdirLNoHes6563/nIDW\nZi4SzvGEYajrFmaNu4RNvSkQKnx/+qO3iJ03L99ofkPAjppJFDp1tdr4fFfc\nQiyNtf1pe/8/qPAT6/ZbOGOXLKQU7DfO0lKqrKfjm4akKuHMJp70fpbj9sYB\nv+Jtyf+vtedNIIwmqloE/6NN00scHPGmblBpfMonSpbw95xTIUyjSZP8Hnm5\nHPyUM4r8rFP0KmFViNFP3AXiH8bPlIE/4rrqcm5X7eJfmOS/7ppiGDFZWqv/\nXWVXxjFxeAKtU1KNJaRzRSx262dKnM0xfloGNbCvvPpYdZJrfLYPzRkklFXm\nMr4iwKSMpWtedMHPxUxyDd6l+qtMYYbuUKJ9uafZFqiIFdyyJvjPkO9l5daV\nduM6\r\n=MVzO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEtwuzwiLvI3LwLR3T2B0fOZY6Cmz/A3x8AyTqWLa0//AiAmeAIMUsS117UwW4S7cyben5CRKpET1gIHYphLGYdbUg=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf83flsl.0_1600424537960_0.8317569736930177"},"_hasShrinkwrap":false},"1.0.0-canary.kf8yuiqx.0":{"name":"plumier","version":"1.0.0-canary.kf8yuiqx.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf8yuiqx.0+01dd8c0","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"01dd8c01eb466efcfa3cc3db5b8ccfbd863c947b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf8yuiqx.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-yerhRj2Fa7mXi7BJLc4v0W4GhRIbAS0Uy3pGkugw3w4FNIg2VTyZIepThl1mKRyKa19b19Us/6SugSgIVM5IGg==","shasum":"e46fa5da7357df731792b0484e6fc0f56effd24f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf8yuiqx.0.tgz","fileCount":13,"unpackedSize":32645,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfZVhxCRA9TVsSAnZWagAAyLgP/0gjGt8euw7eW57rPhKp\ny4WxAafDmkNCOjN2UQ+iVrVw5kYgvMFhJfsiecNBR/p+WdrV/wt6j66TwmD4\nJmVeQp1RZ1iM78W4APXUBhIkITe3hpSIfSUFmR9m/xXTsLZgSaFvpyhN0V9f\nKkRGs/jMN2l7v3v0YI071RPuG1VzMacBeZ1snGz6RAFMb7F/yvsf1MVK+24T\nrbqRL7dGxkiZzcaqjuaKAHa1KvVX2EqkGdvuD52JqHSAEGuR2CI96dUJczCz\nltd+cifHCn0eCLmX0SzTVGdiZgB1WczzSFPxepUvpW3pzULLtJ4FI5L+pZPY\nqblQCCCBHjmCY3I6hPndbySXqE8c+F2Rq+HacuxMOqkYToXKbWlrRQeeQhum\nBnbw/PYO/iJZa67bBEthNzib1/IgGtdId86xH4jHDjpmqPJflS5TwJRmYnlZ\n1r9CKBJqR3Tcmr9VVWJLZUlBfwLefBayS58vuchsGgEEUED+0h2v3Z/4qQSG\nn2caA7fUNwjN0PXED/pX/TNy/XXjlsiFrracdxCnkCCShAObavZiRFUoUjij\nUyRL+4w8f/xUx8C/IZUuQf75iccQmw5ZcFyLpPhub7tKlSR3mjtdsBJUoxj1\nJNkNpvoCCbpph4vHuMGPGU7iBZvZ2MdjKIbyrlDpTtblewbGg65zLRQX0XcT\n0qQX\r\n=tSGw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFWw7fdWnRDaqG9QAXFmOnHeIrrrSD44Blz9GvfxVdGbAiBk3BNbcmLBfSgoSeQPdsv/NLwh7G16RmkYtZAI+L+30g=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf8yuiqx.0_1600477296672_0.3575954650503279"},"_hasShrinkwrap":false},"1.0.0-canary.kf9he5u8.0":{"name":"plumier","version":"1.0.0-canary.kf9he5u8.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kf9he5u8.0+c84a00f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c84a00f979910fffc06931b2d5c54917f302742f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kf9he5u8.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-QANSLb15/coK8ZFF7es70ogPHGmueDAYZvMkKumzL/mY3yz2CU7N2wIr0IDed+mAIbQWNBrq9H1RxKprtDVnyA==","shasum":"87af3a8df676788187d5a8e0332895e069bdafb3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kf9he5u8.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfZdIfCRA9TVsSAnZWagAAqmcP/0GmoJgOSheKOfEpKkah\nzC1boEenffzSxvS71kOycyy305cc5qwTWw3OrJKQJfn3VXhqOpMkCi7dvo9n\n0l31lqg+ixT+ostdejbhH9ot9kOIO8Oi00yjqNWnL81YdcsLC32W6Vh09Tr2\nmlldJH31mAZFD2pBC7iEH7biINimTtMYumBFbciKiWxNMPTyBXvnDLwdM4FP\nYMyWQYEPGZZ6EvMJEYDKldpZ63y0/Uktks/SFRrNaTHEIWNr7jYXhj0sF36D\nYPN5l1P6sFt5uSYunlEG/hUC1fB3aERdneCAS9MuGAFOb8CPP5h6okDUKsUI\nxJdNgvVJvS5invV5CjzDV4JMJUFMY76qmf9Nifsvi5L+xEQzRs5p6WALFvPF\nnbUI1OEkaXMSVuuAWRqpabzzQzD1mm/jQEeeNX2Nv2DhvrqoWY6UYWhtH6Dp\n+HMy3eC8aAQStEP+NEHxHAbLHrgefHXb9uSx0211Y3C/kzl5z4YgeQoqDvXN\nFce3QS9xYarzVI4JHzxERpXw1TYNRVu41xq2Wcs3Zr24db/DjRz15bIybfJv\nH30L5vr/o/mWkuNdqXsatfUI+g/b6sptWSIKsz6kTly/RArgKSFUA/V2Xl9H\nHAgGOh6E/G3VBU4FaUwnsXFlNnYSYQ9rwE5inzFB85hEPs1I4+zGN+6TpvzP\nWWCW\r\n=gPop\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCkCw7JGffAmrS/x54nUzr8OOuQyWPyX8Tczf9ADxiUPgIgJSm9M/PBYHrdTzg7Va08MIeBkvhAaDg/4WebBfjhZCU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kf9he5u8.0_1600508447100_0.34219619813897606"},"_hasShrinkwrap":false},"1.0.0-canary.kfdk5pvu.0":{"name":"plumier","version":"1.0.0-canary.kfdk5pvu.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kfdk5pvu.0+aafe89d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"aafe89d8a496ae6c09c5d950645b9cc3bf05b8fb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kfdk5pvu.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-3OejOy+ug7O2XuuQjwCnhxEHW2EZUV1n1j2cltatgwhXMRirkrfXHiA6vowREDRtWTpDCCaFVoLUhsUgfFwVnQ==","shasum":"a9cffa9c7f2ee30ca78cc27b2dacacd614688fb7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kfdk5pvu.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfaZUMCRA9TVsSAnZWagAA/64QAJbUte9OYrH8qTLy9X3F\n9mswYvRD3MH+d5po4tISy/Fdxr5n5YGldvbws2p6eeKLDr/cfJBGCljt+YKL\nAgeQ+WTwkrRn3R0apfO4FJPJGXYSEGZrvhMp1VqhtjI9+MJmHvIeMsxxnkFh\nAIR2fH6WyL/WjNcyjW8dN1MwvoGQYc0b+Bot0m7kh2c4gCYOXoURY6q+rm50\nNRxhDTOf+QuqKhebnDIWt/5SlHCYp2NP3eo/SFRDx0wm12jiQi+a/99kFyzw\nzNBXZ9XlX4EVx77ZYwBWNfYtCKx4bXKJObgPVbfyOfAfMpsWzqFwQak7KHKL\nqWDdKivkj4GIdg/QjQxkNB80Kzmgy3TNlCUWlkZVeD7TM1ZQgi5RusXyZrWf\ncMbgsRn0niCXWpIW/lG5J2HGZEBbrdBImtq0/lUKB6DXGq2O0BICD1dGUcGl\ngc+jTkmpHa/c5MGm+VPbsSUqePSZUaI0BjT2ZPfjjDvyTLZGWAQdZ5QHTuS1\n3cvkoyYLbfrwqKz9GUtRMDlrqcWy6y62IH5XjZA7ggH4w2YkMC4RmHPeu9kK\nj3FwrvurNDRY9xZ75XUS4csfZv+1nPgFqipEf/e5ESaXQCy32vj/x910Hhrg\n0ZraVrswDXhDBLoEels3vf9yTY7a9Qa4Rst+Ty7n/ECqjntps3/PQxF1SEml\nh0kJ\r\n=C7N1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCN5qDkZxmvwwfwvBd0BSmfxBmMpLBYM7ZsmENfxVnRPQIgEhIS/nGo7HpowuEQmMPUYra3ciJ0Io2cUJLqnVtTRK0="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kfdk5pvu.0_1600754955596_0.03328700487955971"},"_hasShrinkwrap":false},"1.0.0-canary.kfg7x8ie.0":{"name":"plumier","version":"1.0.0-canary.kfg7x8ie.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kfg7x8ie.0+4f8af9b","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4f8af9b20b3a850cd7aa7e8453faa7519978e1bc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kfg7x8ie.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-TC9/NbaYqvC2El1qNxeF6NXadaZAeoE6hxQWnAvPZOj9XSQHx6vsmVdQrCHrs1WsDdMmIJVlYnyOoBtt0rNc6Q==","shasum":"00bde50fa9f455c93c09d7f397e308595b2ae676","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kfg7x8ie.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfbAlaCRA9TVsSAnZWagAAtY0P/0b2IXFfwXa6Gu0sbNw9\noHyXQ+SfZ9eY397cJm34XnBAtwh8z0oM25/foOC0EjFZZGGJvL09I+7sSF8a\nJ12xJ7ZVnMf/To0VYb+1iZhKc+e4DZ0oBfskN0GpQixNeUpmOMPrY5hVSeXH\n+l0Y04lJl5khSXUvN7+xDkO8oZ6Sn7ytaht1XTXeJ065lpiHRsmtPKTg9ts4\nfc1fAYAMe/xxlCTS5FSOK30i/BmsnB9FxbpKBBfIqDLcyZF4rcB67mkmm0RP\nVcsd2tkht1VHB8GUVVe+JkRKW79wpKy9PseyBylNKJT5Uo7ojyjUzC7nvSbL\nIiwmMdD60guJBbLWuxOEw3VpdvXdqKuvqm3MmUm9yjPpVkiOqqYDzvbFzOZa\nTlUNb3kbdm1LGzURU6MIrzM79TLkekzw0WayyJua5FeTMFshfHeeeWTvF3gZ\n2XFcLsLEz8LNVIHXrumC43e7WPnwB5p2SzZYd3zd8Vjdn5A/+wGOrfodSdgf\nKHtEV6ZHibvZeUjEETm3cW5XDvV79wvKePmoxBDCiPrD/DZxkytbUfRCpspy\nXEQDIdCLlqry+8eRZh+UaLFiuOxrudAwLUvswblprdYDcuju0E70yOpv0wjj\nGZu6I1A7+DgoPZoYrN0WB0Ro7Ab/hAoG80gItCfaL944ofrG9Up2O4nIW0gm\nQYb5\r\n=L6Bt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCd14BfXU6v5pcbhGZg/Bg4J6xCpZhPpLZ8MetNJzGYQwIgFHv9tTXSHAx9CcrklF3TG5NBacuPk7mxDrkv+YBiRS8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kfg7x8ie.0_1600915802383_0.34306730142202"},"_hasShrinkwrap":false},"1.0.0-canary.kfku9xyl.0":{"name":"plumier","version":"1.0.0-canary.kfku9xyl.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kfku9xyl.0+4a1b1e8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4a1b1e8599873b1f24c7f30ef0efb9e5d2294afc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kfku9xyl.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-zQTII+ZHcfleTQO7cN/bQ5x4cwYSR9OuXfZX/115yyZmzD/3dYqo5H8VmADcXxVWrK6C4xzxHMw1M6u0P0WGdw==","shasum":"23f66379f7696f58c6d26b6c34112ac94cabd242","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kfku9xyl.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfcEzSCRA9TVsSAnZWagAAlKEQAJyg622ETBExvjaBwMB3\n0GbR7AFY55oC+/fydTKt+7NnVgciMcEyb4G3Ind7Ge/Tgdnm71Mgmluregaw\nRSRI8pr/ooKSyurKLtxUZrw69VTNzKm8SE8bRj6PMUPIEOg/vObrr9zJvP1K\nHQewb67yrCej9DMwabj0lVfsoaZx5mygHI5fCq1HhJylUXNd5ZWY+//PNi1D\nhSbt9LH/7+kWutI9S33o89Mk7bUM4o6nQvvVe7rT6hzs6aL+8zVQqw36Mv57\nqEKzkWANvGFyettigYl/gImYL3MHJUNgwdtJmvIAVnLwS06JwfhEf29/DUrS\ncgCkKfGafcwI52lTe7g0NqC6dcdLngxMXGBHna1gFfkreSNIRsY1VaFYMGLR\nDB5p1I6rJF1uPUDasHrMUHQIVynG4SOTWe2m4WLvwMfnptcHz1G5/wRL1b2D\nPs0Vgz/5Wx032/ZNI2pCjfPNUmo0suGKs63gFB/v87G/o7ZPyQriA4Qz7YXq\ngegQfDBOxQfT8/9BkGNWpQLQ35RmyHa33fd/fGbt2RYYao3u2dXe9iOFVgAH\niyVlW4IXawq4tCHBtWoeqzt84M4+laT37Dv5PuE0hvywhmnhRLWy7NpZPpbh\n+KrMsmAPmhGdoYf4JuvEfSNH/fL3ZprjnuZBLP6s/8qFGUzrdnm8LyvsVo1N\n31i7\r\n=AUK+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEX+b6l1dVmaUB7HWGoQWr9iziytkhxHaH91dqNnwhEPAiEArJSSzt3BE3royl5A6j+saSDsLZLk9Tf9KvZzfqH4qUk="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kfku9xyl.0_1601195217682_0.8955180216897123"},"_hasShrinkwrap":false},"1.0.0-canary.kflwpa3o.0":{"name":"plumier","version":"1.0.0-canary.kflwpa3o.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kflwpa3o.0+4a9a5d3","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4a9a5d33f35a426f2e44c0f2b1dc2006bc61b0dd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kflwpa3o.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-YWcJhRXD7vW8L7dzGZVuwGhi4cOJEs6NNqUuhMW9cZUYqg2yydjeW7n6qekb58mpYNJ1+F5yLKEqGQZuW/Ffjg==","shasum":"8990cbc3a1abf4688e19f310a8a16badb3bf54ae","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kflwpa3o.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfcUjtCRA9TVsSAnZWagAAL+QP+QCoUiqWa/xL9YGbtHO9\nN+phEewJO+3/1SMCyn3UGJA9ikZtlpqze16gqwzb0cB6X31h63539GFAb3p1\nNWsCeqeRM1xsQt2Mflh1zAjDZ8oxH+En4uIJS59+g1IjKXCoIzWDX6C0cCBx\nrGW1V7VNtNeZYEutiVToI6nI4/cofTCmxV42P1t0NwHYP8/F7cgiikBRyJ5J\n2OR64OZL/RBJGx9bQKBPkl2yyVBNWhEqxsRrHbeqC6sqDBCDHyNVHaynWtMG\nUgcEn0ONxSynoBVhr6bWbNYZYlC5Wh6YHX/6unBrkhhsb58wdlDgGB8IyHzs\nLhhrYdCMXm6o5LUZJvWdp/ag227qT3+nAKZuMjrgHXNWt5EfBMw0/Os7G/vU\nckhqAXMRx+nO5JEn0rSrUvWycWJcwvXbR3ioCU0P7wjumQmPYhkJrxUNFTfu\nW8WrR9Jcz0wNZvYU7R4XoLTLHT09IQCPgp02LeLGzHrBxWoZyQvtlFJI1WMR\nSOH7TjJ7A+hzTTQ0EZu7pZPwQP8qjL/6lsow++X0/ovriEqXzn3VvuqB8COO\nSw+izYnKcKduGDNTCNvgps7yvsody3YI1ASfzrsktHbtZCeoHc7ljnikxdwK\nhChz7d313LjRkim+MYj0YugZRiiE/X3x5gedomZncPTZ8J3Zn9FwtCxbjH3H\n7GHJ\r\n=E4s+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIETG9IJ4Yl+ItxgmfRfEHczthuYeLvgNA/K86cFE7x2uAiEAjr1KDufSkd5ASEtlmqBUBP119/VTOnagd/G1G539BdU="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kflwpa3o.0_1601259757059_0.2585248261254429"},"_hasShrinkwrap":false},"1.0.0-canary.kfm9pqez.0":{"name":"plumier","version":"1.0.0-canary.kfm9pqez.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kfm9pqez.0+6767528","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6767528b01ef9de421b5390049cd0b3125f812cb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kfm9pqez.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-U3aaphJqwEAJpi03ESoPk406YnbB8OMZEMcUjLuZxS7dPpx1OzAQamQ7BxfL8B0A9BpyavjcE6eC9FzfpCoLjw==","shasum":"a6d406c28ec6ee61efa5fa283b5b2ed1bc3c8730","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kfm9pqez.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfcZ5JCRA9TVsSAnZWagAA1NUQAJty/qoL9hQP3KeVn/ZV\nyU3Rcui6yZqKSmM/z35JKfXEOEU2/irtPVoTbJhNVu0IxSq/w4wVx0V34/CP\nAHGapqCKWWgufWWONs/HPx7Qt/8VB+cXesiM1snl6gZKgsH4WUjd2/VJfboL\nO8TVsxlbb4cmZ7IQ8uDXUiyc5zMR5PhcQi6R61GtRgSN5qewSzM0m1g13ZDL\nb/54y7eTjQ/fC8tx8witfeDLOtTchvCQogy6cOJ4aFPMT3bBlP+7Cj//IfCu\n8Wx+xAVyr0JsDSf93VMR3r9M/iZZ+wagG61ZtYxsEygkI6aYOyhMry3LtDqt\nBcRAmcczTeyl2+AwTSByrcH8oIQhqpgqytsoDS+bplAnYRfm6/FHj+di1tZ3\nOnUq7h4bWg1SGYMK/W+m/Ec5KjhuMhO5KfJdHvnPBO2SHpNjpBlZMu3fL8Nz\nVfvZrDzHtJiatfwYGzMjmY2LI7x7W1UEStDDzh/eF9H/MGpeGO149rVuuvdG\nc9wCNseQrPnO8s1opZrFCZ93CnfbbaBLoUpMDzjSP6i6VxG7+KJgbC8EEDvI\nZf3aHKeGGEHXMjNWFd1DWKhPVBK3xYkF5fE/o2r1+yASagYVdSBL9F+SzOGa\nMPZTU4Sd/k4KnGtd5caLJbbtkhSG7nLJbpGHdlT+IAI/LK0pLwOoVZGdAeaI\nl0yI\r\n=oVtn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEOnFMQiPfUQdchxm/clEf8MmCFq5NBZ2Gjp3SNUXSiHAiEAwZ8DHBhSYVe+1TlClq+n3A7DuMVNdAsEMNZHlpU9QHk="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kfm9pqez.0_1601281609045_0.6334968953709723"},"_hasShrinkwrap":false},"1.0.0-canary.kfnbyb01.0":{"name":"plumier","version":"1.0.0-canary.kfnbyb01.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kfnbyb01.0+f902a9d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.1","@types/qs":"^6.9.4","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f902a9d0f305d5ab3569f38d5f5c9ee05cf29da1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kfnbyb01.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-M0ZyejPjbaYPIzluEyDFRQPDpCGM33JQtnihsYUZrwfzILMxUc27r55N/BDEYVuOILY4p8oIeED1KksqesHw/A==","shasum":"cc13d1792b4373b49b25789859ad035c251058d7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kfnbyb01.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfcpkrCRA9TVsSAnZWagAAJGgP/iidmU+WiEqXo3+Xal8E\ngZjW56bY1lbkI7CtGXK6a8qJd8huEcmwffm6vinW97NaR504waQxfwhQ6cCD\nHBwG+AO307dAIHTeiNTkaRKl0SURzFxJ6W8eUpHoPkQkV6LE6Uvx0yFpyKW1\ndXnlw/5wqpQUVsC9/GKkfffvnIRRION7OpgvbAxcRBXfWIvcTAFli6GvP3yF\nJSQXtmXwtnfQuI10yigB2FpDnYSkCdIQOS/FzD13tuGuOpkElu+04TRk+S6N\n2ogPKYmRyfe29eRKRPrPZ5AmXsOu2wR4rf7yIzv5hxcGnecryT7Iop4oJQ5q\nWZOoEkdD3fT/yLYVFpFzhnSXa69eWPIUCcpmBBGR4BrcCAHpTAITBbgmYBx5\nhRhSP8Z90fEl1Pwce/vkQR69k6+GSWiD+BGBIRV7WhX0dTZsyHVsy+qNsVBi\nPumIUTYqm3170dQ7vOH9v8gNr5MU3p5ruFTq0USJYZ7vl0hapQZ3e07Z/Kow\npHtBD/wPlTIzEXyUaBPdWB3jwJvKe90xQ58otfnbuHJvT3M46fg7sg0ItJC1\nJI+0Ohw8VJJ8ZWSuP8/vtBPi8M8WSeQ4+4nU0bmdaO2S1kmiY9x1oY9UQTs2\nr03Ojx0YgWR4DuX3xs2MWGOvkoJasnuHSUeG04D7KmUSYiEsYa5wZaeZW53l\n3ell\r\n=4sGX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTvfUqPVLYAc6jwe82V2SwkIR3bgSvceix3q/HQCGY1gIgSnoA0Ndm5rynN/7hK5p5VZvzihf+0fBJUvOrt1wQ33c="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kfnbyb01.0_1601345834962_0.919181949032166"},"_hasShrinkwrap":false},"1.0.0-canary.kfndaw7c.0":{"name":"plumier","version":"1.0.0-canary.kfndaw7c.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kfndaw7c.0+28c72a5","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"28c72a5feda16a8eb65a3a7416b03e28ca95db8d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kfndaw7c.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-Ir9pWWTQQUw5QOZFQ840wGffvPo9MNx1wIucVxf+051Zqd+YR7N7+BdcSiXGXdcgiPQ9G7U3w6cbX0v1iWPC+g==","shasum":"4fb29a8b21d8b944aec098246b29df42dd522f8d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kfndaw7c.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfcqILCRA9TVsSAnZWagAAFkYP/1s+m1Vn+tuUtPeboBm/\nHEP0DHiyQ32VFyomL5JGfcmgw1adZNwnXYvpnoggN6Yj3dXwVKqPWc0hR5rv\nfUBOFswvcYpam09/4rJD2JuHDvdINwzwiMNArffALa2/btfo0egapBe/+hF9\nFdRRntPPi6jfaWvu+xkiRW7ct3gHos5h7cYUKXTIT5qUOwcUhVpyp+D7l0Cl\njU0Zxh+hqLuFrlYUVo6vbAuvKbssBg9OGC9iNAnUr+9SEjGr/Kyo6WNAKALn\nRAQ5T/5l0GzwZux0MFTBzGlkrBN1wW5yiKRD6fyq0rrktylC9xnFpVU/6pHT\n0cyQpCHSJ28bHRRI1ubEa+fxaAGI74xG3ZWmBa50Ot7FcVGLDFblRu5YR/H8\ndl7e5kS8gp+jKT+Bo5jzr9y7QboXvXOrCio/u5fKJW8kDZf/2P2X5ZUMBQEM\nvmu0zQw/plzZGlzVaAyrgr+m1b5q7Dm5C7mw+63+3cGG/B7fcWnEqBRRANvU\n7gSqrL4DYrKE+n1UVQQSgD6ic1x9L6drNywdEen5wxSwsX5WXaBSAj+tamFx\n12eMhZB2jPmJL5KzFZgnjAWV+8hcsEJY7dMRH0cyqXM8EGq4dl/XtuoQn6p/\n1Fde9isp4xgPs4QLAnPpr20gDo93OdRn/BsknQy3b0iv1bunq8fwR63YWCMp\nrYFZ\r\n=Hgfh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICRf8pibCg52TVuY7YCnHw+cilFxe0Ngq5Yz6l5K2S4AAiEAmujQLpZsgazIdRN4GvyE4NAqXc1zgWIcMn8UVg4s/v8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kfndaw7c.0_1601348106784_0.8731925774112566"},"_hasShrinkwrap":false},"1.0.0-canary.kfok0ubn.0":{"name":"plumier","version":"1.0.0-canary.kfok0ubn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kfok0ubn.0+3dc7032","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3dc703277c8514fd632501716b20cef2b2db67e9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kfok0ubn.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-pKsiuC/eN3/GfqR30/jOshdvfqHe9z2BLlwcvwa2YesHSqjolk6/WGGMb6nMlkSz5nONi6XRbrpS63ioXT9evg==","shasum":"c7d5f6fcb7edd6edd4d25bc4e27eca6e6a0aaea4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kfok0ubn.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfc7pQCRA9TVsSAnZWagAA96YP/10Es/KfThW4VQWrjJAq\nWF3KdxOZmRHAyqHr6aCczFJ+YDOzSuKMLnCE2dxGRFQy+zhkHcgUxH82OQuA\n1UWabkfpQNJ7eHcMcCVlJyU2osmSNfFSYZFv0E/eZnfjvu1LpN78spyzx1m3\nxZ4/MWFjCEyHPRtCMtPuoApG7J6zEesz3aIeygynah4W77ZMX+Sw+QOVD788\nSi5VL0H2mkplXEHZZGa2zn6hHKdRRxkJn3N4Hy7cy8VIfpegiTqQL6PftnXb\nfnXCzuiax9vQfwPQs5AH0h9Fjon+IqnEA45GOw/K1/7Vrv2oLJcoM4wpqyPw\nOvVfcLncgyoatIvWJdWdCMgf0hw1L4nQLNDsVmvwqX9Au/PlahSGA9u5WvGo\n/vL54L9o5SMWcYlN+bfp0LJeqWHBBqeQQLVwz2T+fRfgSFbhxr5EFIz+GaBH\noC2jTn4EpL7M13+nm2wcP7X9ZQXwthLzMsV3Mr0/XaMffxs23I76BSsEgH2I\nK96byLZfTG+EYZyWsY9JlWYKqXGQvqKwppv66CMASZ4tTuag6rVQG8ZZNQvi\nXEGPpYwHzNUyLwtY//leKwDGeu65mzN1wqxPD3/Fhi2AqeyGIrCMLthKmooO\nkwyVT9/kOLP+0IZFqFd98ZDkzor8hdvpfXt9dD7+fFiYDg5LR9v8uqoYWR+P\no7Nb\r\n=0mdi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDa5ZFnm9ihfXROUpXQEwPBO1bFW0qFR6Vkeuv2NKfHbAIhANWOZfiIVw1pvKzG88I9/ytLvIaJj2WtFbgObZClH99u"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kfok0ubn.0_1601419856010_0.19054711738438534"},"_hasShrinkwrap":false},"1.0.0-canary.kg3h9ub9.0":{"name":"plumier","version":"1.0.0-canary.kg3h9ub9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg3h9ub9.0+157a87c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"157a87cf9a09d2ddaa868ac63256f3b7fe9cc82e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg3h9ub9.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-UtVeSo1k1+RY1SALRBVub0In74aH7pdXLdtafwTSiUmF4IguaZkF6svx+6hPsNPErfVAJ1aQIb95Hft9rvMe6g==","shasum":"8816255e08b81631886c64e95a94e5fffd6b8382","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg3h9ub9.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfgX81CRA9TVsSAnZWagAATnQQAJDZMCUWtLrkrCWMRfQ9\namW8mDj/dL+h39d8puT9+KcmH4/Hjbl3ofNB8XT6rY1H5dWW5ADFmeD7Z96C\nx/lbpkH4054SbXq5wmxCebj1a53hlFMRBn0PnjNvAg/6+6itBKpFgqg5Ci+N\nmrQm5KnO2TqD/oGDsWoeW+fUFjSKyF7SRKVKpqK60F+/c9uSU6xneysE91Eq\nCOqSOwvq5hvLM4yeXbXwxi6gxcm1+8i+Hn+8V091Y+dUN3kJL0Sj71xua9lZ\ni5w7zRQY9qNoCu2hapUcOINXP2Or9YH1R9iMBnMxDJgppJzKmEJEYOIycnfM\nFiCSfMQbZ6ep4n1UJNQRtWUvMEIp0R8Qs4ts1yl+Z9ynR+gUhVCeZMJpySlH\nXwaqQ9Bll6vnkZiFrY0hPS6ZCCgAwiss2A+Bw0YbjQIkRM1J/DB5Z1Ye+2Y1\nkhGY8bvAX7Ku5tOSYQ/n0EPqbjGZSCoy0aD+RftXRqzJkGDJNswMHekNo6fS\nNR3pSwZ0woAsgW5PZjmephI8sCtVmEpZjitV3+iApvjKYubzgCkaPiEFelQk\npwdgoK6vUTbXgFDav4s1BP/C5DBmf4CTxSdcjLtIgQYS0lbdOaEr5yB5S6h1\nJB+Z/Iy5E85h3u4MxpWt4RgVbADEN+F2u2s97GrSlhWHjWO7+aGRPDihist2\njTfP\r\n=NDu3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqOtnzCnZ/zSHs6bN2MbCRG7oQTtXfZrwhreWsWsSx9AiAldMIEufkJ3gR+TKHLgJ1Hd9NBj8X8/oJ+znD6RFr/Ow=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg3h9ub9.0_1602322229205_0.9230892190614011"},"_hasShrinkwrap":false},"1.0.0-canary.kg3j85u4.0":{"name":"plumier","version":"1.0.0-canary.kg3j85u4.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg3j85u4.0+8f056ce","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8f056ceb2b697c65f3d9b56477544088323e3618","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg3j85u4.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-QSF8ztRDkHSQKWkiB+/BNeTojREGnJg4VmJYKxU6flBpYiDbV+rUyUBRGCqNH/2YrYF2ATmymJ5VJ5U9a/912g==","shasum":"e75970618ed7810b5aca2fb51dafad39aea5df35","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg3j85u4.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfgYwHCRA9TVsSAnZWagAAqpsP/iF4q27Ke3BIMXHkQLFo\nHGxljIVTiWplIP07GvurJyZKlP7XaKPhjvCKMXd+OB77A4JtJS2uUuYlydGj\n2WudZFi/0uPed5vhicar5jvjKH0smeKd8oeIj34oq58Pqa+NSJf/HVhoWkj8\nv2ob5f4oocKIbPMA1hU3wn5HL0Y+prOC1kTKdbW4hipCl7aTPLMer/U+uoCD\nel5VtA5+NAdsWsxQZFekZJAiNK/dZ6of92ZQZ0DJc3nGMSfXOl1hTZAw0E35\nNCQkTbLUSCEhRONGADq5uL8w1qBzGhGRmenuyT/63gA0SFjuLnc9kzRz9dSF\nKpPfEUKc4SZFeHnjMjn/a/PJf4o0f0/0u0Wv1rLZkJDcLVCXhDVuIDWEbZ3e\ndVpfxLH8Vl7m52Ly6KVTh18iDbLRAwKz0VT+wv3wgTe9Brca43W6PGSNJJT4\nGwIWSAMLtXWhk4b8tYuFozJvyN0SsSEkWS8iuUdVBI/iIqJiIHA3ujssIqA/\nT0T5xpoLtx1z0ZEeSa0gtiuliHYA4BHophSS5TQEYTfS72J+c+ZS5y7pKG3g\nj1pKTPAnYF2m2X8GMb1GMwZhrjmaPzN/24OsMjXm7KmGij9oMP/qHkh9KnMA\nBVG3PVEpgZEk8ebmm0rCKCgYz5/heI+qVP/Wwu/1Ab5ULhchY1ZVon+VNvEg\nj16+\r\n=U6bQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDl+BAUIW/5o5mvmmXzf7mQLnwkVgjE0lBAS4pYLyuLAAIgY2y0HHFcj2RrysM9s/GdE9+DG8NcWmZFWyXdHNDhGc8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg3j85u4.0_1602325510744_0.5554997215429445"},"_hasShrinkwrap":false},"1.0.0-canary.kg45m6uw.0":{"name":"plumier","version":"1.0.0-canary.kg45m6uw.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg45m6uw.0+bb14deb","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bb14deb88524bd847bc57a13c5fa5dc58ca590f8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg45m6uw.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-jddW5j3VraiM+A9iGQTdkfGLokgrGhN30kido3qYVC9fzyRhrSVhGxoLS0IsjhMYTPYfD7KkzXdslC0mbCSbGA==","shasum":"9364eea8f9240a7513d2ef6ec1183573fdb28677","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg45m6uw.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfgh7vCRA9TVsSAnZWagAAQX0QAI8d7t/n8myMuxBWdvMg\nimvEXdj8lGtfXRRnsnj+FI7y+8lZHobmHQGKW2zEeP8CUxLVCDmnwiSYfGI6\nPdswWU+Z3qK07CdAEpUyRMNPYF8+IREL9Fv9FW7aKdyxzUw40U0eqrX+ZaAZ\nXAm9Mh4mpHvDOZ9A2L5krYv7O0WAe4T3wJR8U2SNyU7RluGzQyLGni5aOvyf\ng17toTRIIEBQBQycH/SEaGQinEH95f0cFLO8gn7gHTaQm7G6MGcp8SY6Makt\ny44TM2UGguo2A7Z7OzzfBm77thxaguKIO/a27YvmgVbJUb7HNBiOpvhLOgy0\nb7/Vtt2fU/U7mCUCkLlH07+T6D6ftuk2uh3F1ZhQPnrDSEnWPGsosH8BEY6B\n8zym68yybE66hwSK9c9dT8uctj4dhh9M4XkNz+ZGIgQG3Vf5KhX0ocXLK62Y\n/g12H6skmQjD7SEmj2hwLiqjd5KkEzinsQqMrdAXk1Mcv4vgA55WW83lNRtB\ndMNFeP4GDcIGlJDUZgStGCuOF0EYYqmvpfMwo2gi9HYjOWTRPzVVIln+H461\nV3zy7V+YTN+E2rLDU7OXvl4nVuFyT5vdUmqtVa9GMtI5tbgnYVLcr4z+t7HE\nHvbay+iXSR4lIjct16eRy+98wIaQowjz/1ilrxSWsCXqtrfGSToVICrf5o6r\nlwY0\r\n=GU6/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF62WQyqLjrQ+cy2C9ks8QP6Ug7+eAvsLO/QwB97fI3dAiBpzHGD4A/ZxR7YUDqbJBgaytZyNvd3t9HSez3e6G6ewQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg45m6uw.0_1602363119328_0.2392510595599957"},"_hasShrinkwrap":false},"1.0.0-canary.kg4vj92q.0":{"name":"plumier","version":"1.0.0-canary.kg4vj92q.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg4vj92q.0+7822968","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"78229684158ca745ff625183465494fd13dced2d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg4vj92q.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-lSdv8ZStDQN5XyILCDRlATqmwOLilG01/1a5dUs06XUZM8ng+MV1duJnZBuIquEZlxHltAtTWjRSCdwiY+jrmA==","shasum":"e51fea4375eb837e31d8dea3494d0e2de0f4635c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg4vj92q.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfgsj5CRA9TVsSAnZWagAAD/gP/iBRbzQJUcA0xyZkAeW2\nv69I7c8XewipL3ONHgo+zeEt38wI97SBQYQAJKnXBhcQnPBI70wWKmOknDzq\nYqtFHWFwsY+ZySVHjwJmU56k41TIlpy5v6sL6OqhdPBcIR8t9JS4iVeJG1Po\nW5dZBb+AlJ3waXnsAJiExMNBT8wCeCKgDg8F4HWN9b+xn5wEn08fE4//blaK\n01YuUPzKQl0njovWLb5A3kI72logjkHMow4MCEPPai3jeZuLrBM/vQvkNTPN\nvklIux6OYck9NfPKE53SazqPn5kEB7DgjGfaQFdDaTPJkh5hqUmJ3drRvQBI\nMZ/k/pY/rOJcZpY7iF6carVvhP2AjZtCSWAoUKyF/tV8AMh+f3hCS/plfe+d\nzF3LWW90Ik5z4PLVPuOPPug4ArzkOCzxsbQgrjEV2IyX1A7k0FkDvfWYZhfC\nKyC9UtY2UPujTMT+6JjMB7z/Mo1YwVQaeeMhHkdHoclqpWpvGEwEjUTmVVb4\nHLK+vx1SBTPesrqw52F3SwqtPBfscBNbDZwEyRzKV7IfpnskoJWo5lavh6l4\nzjlGVdqKyc/1oLxFH+maT88WZc9k1PqWqrd7GRqxeHlAv/8OpQ9IrW1JbDvT\n1EePfmPi+BQI9gjEBn1XlAxe6EngtGNygaFXj465mtOUyosBnJLwqt7UNUlM\nk2Aq\r\n=Az2w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDeSkd4Fzd1M5u6khzkavWDDaj2KsUV4ccaZFi3Ujru8wIhAIYZU979W2MP5xXv61UKOYZWIyKSzkHaMg2zQPUgRnmq"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg4vj92q.0_1602406649131_0.20213684019823663"},"_hasShrinkwrap":false},"1.0.0-canary.kg68uhyf.0":{"name":"plumier","version":"1.0.0-canary.kg68uhyf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg68uhyf.0+de5850d","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"de5850d5f868bc934531d942a0972fa4a1e1f445","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg68uhyf.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-bO1ILw+XXJd7gx3rgu8IP5gladrLozXFr35Mqg/IH90OO4NsnPCvy1zVeKdYM+4x24Co2NEJJjtlAZvPFfGxtQ==","shasum":"bebbbd547788a5771aca7747f97988ec1d3049a6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg68uhyf.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfhAyHCRA9TVsSAnZWagAApzAQAJFuR+q22Ac6x0PMHjHw\nqyt6FYxiOmX308gPqlsqrb4zNn4r+LJsy3G8ar11gCX0EHlKKhM1nntrok5i\nDBgcAL8WKgC7V4rP+gIxB0UbYyNTaunAEBBNT1gXKp+3lsjlsWtyAtnkOQy6\nvJJ4YYhAikM3Ao94TI2CY0OCSiHGkf3hRu5i5f10y6hBIZY7XfAAVcjZIVSC\npEqjx+t0hmgsu+1zYYcGjNP7tbOohKlg8FIuTRRT0gvzxPOKWfLI6I8TP0i0\nT9cQ+nFUYgTW/uTc8S+wU2yrawuAxj97SfEOOm7VC5S/YnBSmIOMpoC1x7cT\no3eWxgdmVB0NhRcCHA1nZ+hBHd/KNpuqt/zTPEDBqxJ1KsS0QPc4KGyfHOGf\ndAsx4fHfMigphQ5wF5eK5CG4Nr0PywJa2wc81p3TNwXn0iw6nHSO4ghpMuon\nyNLX1SBs7ZHwFzHgV5Uv8JalN71XIi9Q0lNZgCLDHcr6CFNUTFC/CS5fzSpQ\na0/VpLsthaVQxFUMNBnvd32vLm0uBIdfZN9l04pyRW/5G7RzeOEmoaOTbfcT\nJafqbFFgCFKkdgIt9pPGf0ZkhG+1VvsIFlpzYex368S9EZP/NxwEuWBOLDqB\n+ThVuFYZ3pQvfE2YJhqKkDaSIYHlgnaLyg1BcFceY4hHGA8u8gNaibvC/E4k\n9duX\r\n=sLRy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBFeCi0jHaOj/nRDA5Dup9zl5+XSfFi7xaCrMb/ZWADAAiB2gQhO3fa+5kwzgtIwDb/R36W2r+sYpgnGe5LUGua9Pw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg68uhyf.0_1602489478792_0.11312543905705219"},"_hasShrinkwrap":false},"1.0.0-canary.kg6g8g5u.0":{"name":"plumier","version":"1.0.0-canary.kg6g8g5u.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg6g8g5u.0+f036b60","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f036b6002d1e6be78ed717bc328045f2df92c821","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg6g8g5u.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-r6lTvUl6jMhzGOlAb9OuigwPpBc5KR3DnlKxjjFVkNLY68IS2DaZT2ejxsHhacx4C/lSXzbLfmnaq65Q53UpNw==","shasum":"398f5421d1a9855a8555f88b2dc94f19e4b141fd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg6g8g5u.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfhD0ICRA9TVsSAnZWagAAlagQAIdwb8ldnVMTS2lgz6QQ\nPCFeNdDGlCaHio0sCUgQX4x3Ipb0NDKOuRQinmZ+3WrbXg7CWy/h+WkPatUn\n+tA6nlh+Mi2gE3onRgYcyIxifP7juGQhV13Hw+PWX5rS/JozZoyERDwsZC/q\nmTuWPdZA7o3exgbqv3PEBcBVvhaNxINLcMQzC/9GtuBH7T/OF4L6IEhiZkNU\ns3hVw4csIdN0qtQpnkxSGCiN/X96oFykRPBBq27+YhdQiDUdAgfXS70+iGQz\n/ta2OOQ3dpuAjGXnWkZaueXck8r/fUYCye08VqyJdgOWQhWp7jkh63j+hWPG\ndGrLioNI0B6U0GkzVklM979Ud005bZksuPF43YRoruAbCWp3GuDbtvcwSzTr\nesRi6hF83+ryxiliBdhhsemIVKC4nImlbN5K65+H/aS1E06jsJOTXLXzZ71l\nPOjheKzy0F1Pu2DVVaEH6bJsIp285lxDwUSAQJM1rcMJP5BYsrO0W99iMxwp\nOrVNi7lDcS+fCXaVdp7eL0vIpw6V50+p2NAZusYqn/captkuJyyEDYfMb5xM\n+6C0DH9nh6BKKan3I1OJ4QyRd3zjn+iKLdI32D3gGx6Z9R64CkeaXEzSyBZd\nnaVUmPUKJTrxnXLUKuO+tifDJuGVsxE6616lkrLcyr19adUmSJE7VZH5W9RZ\nRB8G\r\n=25AV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCoEEaJK5o0AHIYQLvXf41T3wqgUqtX9Di53i6rcgG2HgIhALvmc0Zt9lmCM7lqf5CwhiOvZE6hk9cUQ6IazAqU8Dx4"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg6g8g5u.0_1602501896401_0.480593912398142"},"_hasShrinkwrap":false},"1.0.0-canary.kg736xd2.0":{"name":"plumier","version":"1.0.0-canary.kg736xd2.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg736xd2.0+cc3f7d8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cc3f7d85532c68b98b1ffbf5757c8bae2acb6115","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg736xd2.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-2Q66SOm21qBO194s4bo57QRJIITI+i4rVB7o4n7xx4EKcE2fLvzH8nMYAxYcZf9R1VQA5KCpar9jVSEyvCBT0g==","shasum":"97692f878cba50078db2471d799d3cc7958060d7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg736xd2.0.tgz","fileCount":13,"unpackedSize":32770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfhNOcCRA9TVsSAnZWagAA2TEQAJxIuJ6e2aMtUoj5MJKP\nL0HX4S3hJkPlSu/aLolOhXAShnZC3ZSyAtfanKM7vb2hYC9rUcQ3E8gv7fvA\nsEDQAEFiSmSvU9sQdC7FZsc/2lUcxdQdqw5Ziv+X4Ra2J9iO7YFVh1CcQDN2\nFf2l43nx2s1mJQnHVO4zH6R8APbYBZdGSO80L45REZwsiajRGLIzvVzvzNHX\nuMJKR7pFdBBFEXpKk+Im+CkTs64RCl0uL6MVmy/w4lCrTRVdjOgql1/FPsfT\n1ZfcvF8iOoCFw1+Qd8ZCjAEDQB8VTTPq84PBvo1gr1KxrC4akwIn/OA8h5fZ\njxmZBSYbknqY5IQqUPXy3K/qGUIp/EzOOUdyASZIxxnJVB/XEd/pSfu/xIXx\nfu2VpknY7rmemCzpA6drHMUQp4ZymffxwHcsYX4YRrPx6msQncI+SuF0i7Ez\nFX/5mJTNEEsWn3wA1+ea/EvrJipO1TPHdkxzE7mpl6EYKcev/KAx4X+RDtYB\nNKMD9AuzOypK/n2sXoeEPdIgb2HCcrXpBCiyhndRem7ROPEugH3zquR8mGqV\nHwa9mzYARRWmbYcE/iAChEQ1hdk1fbkKLPldwCXWa8nyDFGJbTdwmi1snJu7\nO9qsetjfnqRMiCvv2YTe5oJBtlGxC6jZSd5B5oaUvhMRX4ddfF7i9ookrLOD\nC7bb\r\n=A0Zt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCSeX/o8JOjQInVK24H2xpH5pTJFYrVbk5lFQDD5u+JKQIhAMLqL6zFDp/a3N9XPlge8W48H88fsIZlSUHC5h79OhTz"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg736xd2.0_1602540443869_0.36443737986060865"},"_hasShrinkwrap":false},"1.0.0-canary.kg8ugq0v.0":{"name":"plumier","version":"1.0.0-canary.kg8ugq0v.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg8ugq0v.0+46ba1ef","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"46ba1efa9aae70684307289d23f79feff595d553","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg8ugq0v.0","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-xQwtA3ra65UtAXbsXnxQFFEHwH6fRBQ3cMmtALnPqLfwrZ3urVDMuHh71xh2K7jxk4XXAf/vTyBmRtT3x4vxag==","shasum":"287ee8ef6404273dc5ab6c7169a47f3668c3a617","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg8ugq0v.0.tgz","fileCount":13,"unpackedSize":32809,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfhnLECRA9TVsSAnZWagAAhjwP/0QT4uaXx2oP4A5El3GI\nzdXKtOzPHE08EINH6QBXwfQRaM73gsBpP11kQ+h4bh+Equg1aD1ZcYnHu/7f\nt+bsX5JgpfOUdSQeXiQl0A/YL6pONIHV0R4MhCuBx8JSdkN9rznwDigB0NdO\nF7UzaRxYtWl6ptKKq3RSgu1ujXI1g+hsMCT10O9BVefL2FhiFkHAGH0GvFK/\nq9jNxNUVYwyd1+gm73nDgrgV1A1ohyyl3LNKQ/lsyYoovrOyec578SymUKab\nR92Rxw2jUITlYQ5SKKcOosj48TM5ikl4GYs6h96Qfl32FfbP6PEASzXELIYn\nIKdhaGd4gEHL5xcVcXQBHeDM+rwd/XqBKBzHO1b2YTpW0mR6Z2PPT7Ym+q4v\nzdUomIMLuleT7EJK6v/dX9bH/dtejtNNJVaJ7iNHkdcXiiVUo5GI0bhf44rQ\na8XmNSFk0mRjqULhfHQ7ofcQNJdhz7kYd2HtRP31mYQQINsKSwemtSMOS3aY\ni4lDa5k07D32lrW2hMqYBzFnHgUfmi/aZG+6L/iGPQDMoeBaiR/Q4Y/XvDVu\nSJsOqycGPegoeWRkoo/JNitCup/Na0M7z8tgJKiM5TYEmLuEuafPfHx9b6bJ\n/x6VKry4yhCx/z1rpPzAXmp6K4z4etxIyLASW4x1UT3aPubXTHvknhq+aCRg\nktdy\r\n=m873\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCiizMw1sXQMSjaOT3wto2TXT6iIO0A2KFhKiSbcJRCkQIhAOclUVP/43xijhs8yAiosd4vFGxwHA3GCStTmRIJRjV7"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg8ugq0v.0_1602646723886_0.052089302023890616"},"_hasShrinkwrap":false},"1.0.0-canary.kg9x6kqr.0":{"name":"plumier","version":"1.0.0-canary.kg9x6kqr.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kg9x6kqr.0+08df864","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"08df8648788e5ae4113f0294d27f9e33fccda37b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kg9x6kqr.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-fXK5EOYhi8s+Etgt+rQ7yEP+laZrwrObOQNZ5+3BP9FdrDRS7n/YBGnbOX9T8J+PstrFSq6k1VI30vkAf6osWg==","shasum":"f22c01fa27b63db951fbcb29d7eff3b75f049a4f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kg9x6kqr.0.tgz","fileCount":13,"unpackedSize":32809,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfh3DECRA9TVsSAnZWagAAkvcQAJgcrI8Y4wyL1FWsN+lj\ndHcwCAzZ7BX2u0/tNTVrKsInLEXJB3wajyyny8U1IVX4+vf5+VanLUAdhYfS\nmwO+tTW/8n1TUkJ7ObPyCqPYLCPuF1afS+DxJSTyLSz8PIs920Mr5T1tTcJ1\ngAoHWnIpox/CC0WUChQa+CSMZ8oPSckNVFQGVKPAoPOOPusemzGqS/+J1V0w\nMES1U1BSMKP8kHJY5O+0IvFL1h1e8EbKrTKy3/X3wUElZyPouA7XfKGgObfJ\n++zsbYZkMmm1n8s4r6oB9J1WsWWuiXa8QUTsc+drZHnBq8X/aaFX8eDIyGB2\nF21V45/J10gpdZ8RxmWSRKNL8vQDreanUgchvS1iSnLKpMu4J1x4WR0FXV3f\nvRo2KnYt4tOr/EBjiNJQqYRDdI4/umeyJ05AJ9FdSMyD6JPJdlN86DfTNY5H\nrHQvpLncGSVuUXjj5lUBkwyf9iDXyrqeYuu+ULcDCWlBo5IsXPUS0eQv+Atv\nVw+cahtqg1nKKKF8R4Tl0fBP5Kwyc8VcyT2ixADLt9ONuDFCmLGpfQsCVjF5\nkr5i1eepr9fQibX/JJSblh3kTtHxgeDJF2yuSnvnFqjChYbWXG5MFxyuOGGx\npLnaHdfWxeeQ3+NEt1Rjs5xwDHVmoNCb1vQF++uuOeJ6SpHtlSqOzOKSOtGs\nubIm\r\n=/lKu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGXGEiO0a3uHs60URPJeJ74rSdvLnDLxHdY62QvBFQWDAiAh0B5m423zJnANE7JHC3oe+P24gm0druGkw0clyEsYxw=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kg9x6kqr.0_1602711747945_0.7710792037653353"},"_hasShrinkwrap":false},"1.0.0-canary.kgbtqzo0.0":{"name":"plumier","version":"1.0.0-canary.kgbtqzo0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgbtqzo0.0+debc056","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"debc056cc43e23ba9f37f80a7e84efa268ff5440","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgbtqzo0.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-/4ZBYo973/aGBSBt6lN24uCExI6jtLmdcVIsciMhFKYqd21Cs79GZMmpSRN4KvdVzc+SjL4tCgZLP1baqjhTGA==","shasum":"6d76304cbf23555c88e26d04f921b22b0b14c26f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgbtqzo0.0.tgz","fileCount":13,"unpackedSize":32809,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfiTKjCRA9TVsSAnZWagAAaQ0P/3DdIU/FlcwAjj3d/CV/\nTpp9Aga1ABiepgEJdCRq1/abArFjw9NYTSJasFSyA/j1mT7j9aXx/8YmVNwZ\nNM39gMOOLbc+2G6GMYGdtZUqr012Y9M9OxA99w97lfWyh/c+4DYEJZRKiNQ2\nFV1YOFdS+lQK5O88Jpusj3ShVlcrLhXla2AKHaks3MLlum8aJ2+3E1C1IXhU\nHGVr4QJovMwxPmUaVYCZqtYbTd+nlyz0r+rPrTfZPHVIBAbcpawquxvvwdWe\nYTkP/uR/TNNKms8WiDB9+U0oyfSNmBjoXl+cjT4r4niDAelrV9QBNtuRbtEq\nQeJQirORE1KmFZntbIaZcSwSWI4uwPGNLDMXNxNT5YRm9rrlYFnETbmYmKiD\nqruu+d4tgueLmM+ElPCuUjlL1gBgtXAKMYx5pOzunExcq8HZEr9ttNvcVouN\nzcDI7YsiIrZd4iGkGrcfOLK0v6EZJR/KGrxCpUcOMcuxkIDnqngzNOyufV7U\nS/0oXBFb314/HQL8FyBPhuzsRraASjQ2BvmQnADU6Cs6sNodMjguTaCl94td\ngRtenljAUC7O16REihxEDFVFMlC+CJDWsTECtWvf88xlMAklDvL3miuBZ6kA\nAxG52Yt1ZOAvFdMNmAIcU0HXXza8TVFbmfxpPl2Bhbq3fMZK8kEBE9livWaK\nzHsy\r\n=IgsR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAaE3OTqUdQ2ssG5+pQep4OOA6uWc0YixyanCMKjfxbWAiEAzz6urtJysC6qEU1uaiVKnNNMVlnLRYQZOAcWT2NPcN8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgbtqzo0.0_1602826914847_0.025827266800692472"},"_hasShrinkwrap":false},"1.0.0-canary.kgc219pd.0":{"name":"plumier","version":"1.0.0-canary.kgc219pd.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgc219pd.0+6619528","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"661952884ca1d89be1d1227c7b3a71ebcd5bd1e3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgc219pd.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-k2W2CTZw8h/Q8MOIn6zXz/3cQgKngws8gt4FqNmhnr1EImozTXW7/yp4ofpKryTexvwh2Oq2PJ8kzaFBgDIi7w==","shasum":"161a1d2975c7b7e966016c356245312e56f2bd28","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgc219pd.0.tgz","fileCount":13,"unpackedSize":32946,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfiWkCCRA9TVsSAnZWagAAvqYP+gKNwNZiOCwYOdnBnYiV\nIe+rOEtsxbURH6gAtIA3C3DCvqdcGMVNr/iUpP8XtnDinCl8EJUQcRTKcVaB\nBsYVR1C9WBKLSXEJZVXiEIJZQXO+KSwT69xIGr4WwodzjNVcnzbpq47dwRYf\n/O5KN4uAG9oRiLEEVsJbU1HfPLPtUEER9vWw2iGReX1c/zVpL0wzsHl2G5nB\nroF1Sw4ZOxx0YsiDYz9xtz7V3Vgz3ZIuNOjKYgZabevN5E76Jq12rlbpVufv\ncfnpNtwT+Ni/Q4NDEU56aNycj0ZXbqDhNwSpyPCgbMUqlT3UmoEhE253W/sT\nXNyRhUtdmd59j5hCVXzZdElB8LdaT6O7i3Z8MYwRihZdhqOrzC5SnBLRGMzo\n09NvFQpDgPMtg29eeyLLyZdmgKPn6gAEoW2odiqbRlqC/i4UmVECx+FY659M\nyOaPHOLo9Fss5qFspa85KCaTZUBaUHtdEaXok0oKc2uOPjsKWMZ0c4VaJ+7+\nZ/NVfeGwAnTEC2T/Ke00rv+gQumwCU1AH7DeWflEBbAG1Eeezg73w7BYsyjy\n1lQ5tAN1hHFSEwD3AUs3/NXES1jGumLysAGPqmQQvxuP02QmASL8ymqlfGER\nCEBcf1UHUGlN4gYo62c98Y9lUP1XLEj9oDWn2qCDdY6Do6KEhpDB0ZUUDOUQ\nbBYK\r\n=2So3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHDCJvjXKIGJkfxPX80IbSZDo0HXNPmZ9sJ+LPOmTTXYAiEA5BXSDxpozGcJF5ACnLuHcDl90yo78F2maJWxkfBV6PA="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgc219pd.0_1602840833863_0.7141620508762421"},"_hasShrinkwrap":false},"1.0.0-canary.kgd3ozpo.0":{"name":"plumier","version":"1.0.0-canary.kgd3ozpo.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgd3ozpo.0+aea6d6f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"aea6d6f23b3af8191372b9619c411bcccd2f1809","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgd3ozpo.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-RiP9cD71avxnFFZeQWL9/pUJqvifI09ufE4zlOwX2vKhKVGz++RMykqPctAsKONk/a6TWZkeM/snqB0hy7PXdQ==","shasum":"fc1f4e9124812afa0eb4ed0eaaecc4294aad6b7d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgd3ozpo.0.tgz","fileCount":13,"unpackedSize":32946,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfimAWCRA9TVsSAnZWagAAq10P/1NqnKsbojyjxurv56zi\nNLb3qcHCjiNGUEOYNedA1wp88j10etWKVuPCHGB4+Et+CR5qxZTSfesW85lW\nHQXtOyjQvYpXMgrj4stIqYf0eVotToa80gFNFiuNcM8/nI1vQK4VnhBKq46f\neUmIwvQjfF78b7fhOe98ElK0rAzOCV5t3uqIMq7w8Hw3bHQ3YgKiL4QFW9Ia\nrsmyx204zgmJ5CjHpXcOvO45wkvqzT/E3uAcnNJXqSTJwCqMABi9R4D7byML\n2wov74dfiakBgaYtFn3Iou//kwswmjUtLy4zcopkV9Jj5dLlkKTwziBYIuVh\nToFSGhPiEKrGHgm8RoDYDzd1MeYShyYQiWTGbD8WsIiEWNJ8UdmVTsLlXq0U\nBM9j0jTMxRXZ1tIcXHalFwtLh3PH+QvWBRSVBG0qg5YW4Vi+mJeS/OcSL6bd\nesAZsuX4LPsT4yCXtuQdKq8KgyIP4jt9q41tDTznmmPlcwvbQOB83JWcwFhQ\nUMYp5Ag+FOgLNW/V4KKcNbrB0/AKsgQ4h2GWMODoptCEZoPzWuO0wg6tr4Or\nQ3nDBV8MrXdV8JgiOMf0zSILMe1E+H1mZacMrD4eG0KsuKH1tIDJqgPyBK9g\nLh1fq50iC6580EXqdigu7j+Y4seiiCrJdmX1Yk5+buWEvs5N9FvexemkK270\nOFYo\r\n=lhrV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICMuMYCTQVqPICsSveEHdweWLyjRxsoYxALvB2Jz5wHUAiEAiF86tyMZUGQ66+EYqInxv9AHVWp0T3N0JAxf7ZC+bTY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgd3ozpo.0_1602904086438_0.18236852161649608"},"_hasShrinkwrap":false},"1.0.0-canary.kgdlm368.0":{"name":"plumier","version":"1.0.0-canary.kgdlm368.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgdlm368.0+72d6510","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"72d65100fb29d00266799061375ed45b33ee2bc6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgdlm368.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-azLUP+z8GbrcvQ+aOOZxJSSzxsICsh374wubTaTBnczctWsgq+3eJY+jqLhOpg02B37lYli1px5IqDywWmEKhA==","shasum":"d6c41678bff9096b86911f4017029974b3de3208","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgdlm368.0.tgz","fileCount":13,"unpackedSize":32946,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfitWlCRA9TVsSAnZWagAAlUwP/1gEMLDivTXXLxo6fpAF\nlR7pyVDDwKLH4zH21iPajn8LMUwKmzbP5EizA65Q+HZJoC95oLr8O5hks8wI\nlCo/vBbKAQ0fNq/R0QZSjDXlbrG3QSGrMM/RhuMJarEDAMXFRUqttABUrc49\n/04fnJfJ8hw4qqd0yySseAdDBxQAoQS6U8lr8wnsOhkqKMF3jr+wKUVNuyFe\nXiuDbdU2TKQtozyLWylbFpaPoeUgLOjD3rrj8dTeXIM3gCeZXmaZRH3mT3kR\nNWjwa54TIROh21TwLPJOO608/l+vXePJGKtcisSOd9xbVve6wPXFJi/Iaaoh\nDV73/gO1FegE44FXbLYVpKPI6/0ecC5hNaN38NVE7o5KmuU4856Amh6mEtlu\n+FfOvMxSc+Q93Mj3no+rVOP7M5t83vQZQ1NlwWqNS0N9DAXNpq63KcX9qaJ2\neTtPhnX05CFc5vMFKN9NVP8ZAH8JBJXOER+D/T0H99BV6LH39BX3QubJFtJf\nJRasQquDGchhUvBK9jurg7fYy16G1+FSs5jE40S/jc+EuAQ5JMjZ86Yt4PzS\ngveZVq2Z74d6LUfmUkCgVGngBAIViQpklZHaYqFUgRjQHutwfK+ihD1mui+m\noljAA7s5FdLBo8dvVb3+/wZq0H+hLD61QA1T3gsii5q41locmlI5dptiJfrw\njyNK\r\n=Iq0D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGffe/zlRZDcj2MCRYL+C2EdsDig4o9Sp2xbkyb7He2mAiBNwJKXY9QMrLzEW7hI1Ic55GQzBFyyYALDc93AfI9a0Q=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgdlm368.0_1602934180612_0.15479867067947928"},"_hasShrinkwrap":false},"1.0.0-canary.kgdmgzlm.0":{"name":"plumier","version":"1.0.0-canary.kgdmgzlm.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgdmgzlm.0+513188e","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"513188e5794c339ac9eb66c4b2b4063070dbc420","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgdmgzlm.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-KRVACb1/lw8LgVj4GsFixHu9y7T6D0OW467OJOc19s0e2EQqppDgTJ9pU5Xcz58S6IBzsUGJKppP9yXn2S0XvQ==","shasum":"4f72f7ae6e5b6ea29147e88b330e513210b3ac8e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgdmgzlm.0.tgz","fileCount":13,"unpackedSize":32946,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfittGCRA9TVsSAnZWagAAFrgP/0+onzq7sw6UnjL8xGO1\nLpmbNxkzwAv7ZF6TZK3AsquPQVQz44GgHMyXZ8yzvodZiECRSghn+8OhcBue\n6f6Ix5QBA0rDt8we+I2Y7xnhGsfFApikdNVeg+fhAJQHKBzdK9gbRSIPROXR\nXZ6xVxwH/CHhHujMeAbigRKewuefiQw3bgT64RTLuAI1hK8vq7tcVPEL1ga/\nP2MluJRIIupSt3b7+NNhhfQJEEcvDzxOd6RDCBbTAo+qdIa+WbqSrMjGcBwt\nJq3iCDDH+OeY0cXiq4C7a9cpPofwHQcl9X0UB88Q63WU6SP1zepDACv57kqM\nIqobikRCOD4ZKDwfCjwMTagqGNo3FE0vKm3ANk+VB0EVyr4htCvCQRu6p27k\njLYhDgGdKtqa/PRwFxT0viwhIqJAeUu3B0u+5/BkS5yXNcnJ6XkRKGXtg5aW\nY/eapbbdIEQZ39IZLUozmAsyGrAjfjHTqkOZaYotn3PenGu72qhUOlqAKvu5\n1enfB/gKfjR0xAOMh5poSPsDNfLPP3/dvs8EThBvFTxr0JhCmIsDLZ5t92Ue\n89jJVdgyMA4E2jSFrP8J0ixGQrpZO7cdpqw9vN+BjS1H80s4XzePYf8JJpfs\nvx7f0UMmymmGahYIQCa0vtRgJLzqeOd+BuP6T56DGdcuDV1a2u5wl1jXwb0W\n/2h1\r\n=0k+v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCegZpYe1XAf+iQhviFgC6CJ7EwcAKTxpE3CREQpoN+RgIgAp067D8dy2c+uZzecHttq6HSpNJfw2tFI+MUso5Twws="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgdmgzlm.0_1602935621817_0.6841445758643216"},"_hasShrinkwrap":false},"1.0.0-canary.kgea9t96.0":{"name":"plumier","version":"1.0.0-canary.kgea9t96.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgea9t96.0+96353e3","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"96353e359f27f36466ca1b10150e1d03915d2bad","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.role(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.role(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgea9t96.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-MUxmmoaddFOZWxn6lKl+vs2y+GHPoeEwKiMfMVVSVvTg/ypQ727FIcueh3Em7jscFqTnZe+dpv+FVqT4yn2PIg==","shasum":"804d9b4899ab6943a4e607d2009b3c9d4fbeeafe","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgea9t96.0.tgz","fileCount":13,"unpackedSize":32946,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfi3dxCRA9TVsSAnZWagAA658P/2gH0jhmgBizMcbaGwGj\nrAIqi+B18vLvJ7DNt72X9mEJ7/T5T+cigLwxd6nX7xIvgGETrHRPecBWiZfN\nZTCq7v4iavpin9BYXTQIVCdswrTxQjCVtgIcUiLKco5mcT/h25bLczUP7SMh\n+rW78Iov6fS3AmGdJKMz+ZUN42tgGREaQrZEPK+WDY6w45CUv7VJBKsTRe1R\n48STBuy8y8SwL+kSaI61+Pf6ShP/4srD/gNebJ35qputSrMVxmxzQaUw5OvW\neKGQwXOUxuO4X1GxzRsljvou0ND8C3NZkQGyi948+ZA86zys5I5lToVSKQ1t\nXMown2dTKKaIaUCovwWoU8SzpZFmDK4hKTyA0MiZpgzwsNpMTkWmdbmyL8Ab\nikmZbXNsd/UNDp4lFjE/SrpgtmxYs1TNEJPiiyiaZVd4azs5gszsuo0iw9gK\nSCdmr17N07OxQ3Y46j1zi1h1OZlJ8ULniC9GmbDmAPHlV9ynQqlemFxsxl4L\nXQ/vrUAEYARJFmfEbkCEQ/VaOKWgmABRzOTh3tfa4wC4GHYDwjX5pk76lCPp\njGzIHbXO0t34oWHQDfKi3lqBkKXdqwiMdVsTmUGMDMF4UnimoZfzJpvyXqS/\nMmBfkXm0ncncb88RQcRRwBvrNBYhm6U/9gOTl3dbtRwdLkGBVqZs3jQZUE6P\nPr9l\r\n=HZYc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDezrWOgEux0Nf7otyrDFfzSqnIMRR85cZwCiskVQ8zuAiEAifn5/JM0NWYu70d6pr+bdIwwQIZWmrujim0OEhahA/g="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgea9t96.0_1602975600524_0.49049305114902597"},"_hasShrinkwrap":false},"1.0.0-canary.kgelgslo.0":{"name":"plumier","version":"1.0.0-canary.kgelgslo.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgelgslo.0+bfddc69","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bfddc699bc980dda689b67df01af464c828c8df3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgelgslo.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-lPb7ahD4qIooKoh65+u4ITYnZaTwhEs6BKCjg7ddg+8AL+Zsv7QnV3DnKXjZTVJRQcTm1OFYNnTbN2vehjVTqg==","shasum":"b9dd00f433319299f4256ddb60887cd3ea74535c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgelgslo.0.tgz","fileCount":13,"unpackedSize":32948,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfi8DiCRA9TVsSAnZWagAAedsQAJCD+MpPWouOjP84ud/D\nWPD1MuzbIfFoH+OVtmjI+676lPs0aY4L0J9tWardoszEuHnwX1DGrwfnB8DF\nWWbT7fzal0d+zg3q1FUpL9ObNxSKpZ6vdpMWSMnzdov+siR7k+qOtUCvwgsb\n3tFWYvLPXduoKLMW8mHF2Bux5b4uzZvNupC6+2OGfTLJLuINTarxW31NI8d0\nPmFsiT/HYYm1u3rR0XFB8G4Q24mEAi4YHRH+hnpTe3CptFOOVYA4UnDm3xUh\npelBqxvvkWodvAZtXJ4Y9hIWiUqF5wnpsfdgXStCIjVtXeNSeu8Kpenouvwy\nWT6Nwcooz4E53uKUaLnJfVApMXJ4Tz8goxl/rK9HbRZoda/Sr4MqrRchTypf\n+r096D+ibl+qYi/yk1zTQ5Z9VLexS/GRsA7tqXkKkJtyfS9SmhwgodLdodhG\nsAs2Y7izQG6p8547YR0AvBt7hgF6l0HzqLMF2hYpIbCl/WVfdWhJpdyzAIy4\n44NtrCb7FInWGb2y4DN7ruGHKALbcz+sZXItp/iXqSZBGGK18OugV9KsKuQJ\ns0fBZ5295wH+QZ+vPRwjmD6aYpIqbxIgjaxRkULgmMBrNT/Bx6Mw590g8j2u\n2g8ggTUg3cTsZLj+dUxZxNPhP06kCtGOfKgIv7Zrvz39Ug6wychu5X/hUFi4\n23k0\r\n=FqPB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD3aB5a97I4ocTWqTeVvOGsGucNh5stwvLGsPVIWXAADAIhAPIF5dGDvGsfl/eSdE/fk/nJhWORSzYII/E8Bwk/Ie51"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgelgslo.0_1602994402112_0.8087728883192702"},"_hasShrinkwrap":false},"1.0.0-canary.kgevjcoc.0":{"name":"plumier","version":"1.0.0-canary.kgevjcoc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgevjcoc.0+1178af1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1178af1cc765c0d5f0976e9b676636e243b8cf87","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgevjcoc.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-lIedr3oA47413Ud6mI0JwZ07R5NkC7FjuQ4iLGuCjoBbqLt5vtPbcyykwwts1vur15gOM6h3f34JAj/GRnRSeg==","shasum":"b1be5c3f433be93727ab9ef95fcd0af21c3c1b69","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgevjcoc.0.tgz","fileCount":13,"unpackedSize":32948,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfjAL1CRA9TVsSAnZWagAAQREP/jQXMr8ExXs8ojhibwPn\nPm9DMW332PyKUULbhr+/ENft3wTRF13qmjmUhkElVVVAFn1HE+OvLpX95hmL\n3XiPa0LZNb2irj7yGX9+fW6s/ejRgeyV+4b5P51Gm4HNhtTcSjDcNHaiHSxs\nKxY5Nz4xVGVD8XAcvFeM6jKIxlG7afxgnPXrHnzD6rWH7a670WD84yWV6W03\nqY9xJufddOx5XK58OUIexMHfIGbxwHWSvCmDLyfDNPzTon8lb596/CukkwuY\nTK4FaP3+cg9at9vVitFHZCctS7TyvysQlpPJwowAa//xy+K5gnKj60nsaxNG\ntVUaiNufnj8xwvDIB0n+kbntrW3DBXaH32mYVoNkV0wwn0QXls5e/Mn8oeJu\nxkoVHhwp+AZvTT+D6gV6bKY1M8WnhQcsZPu9rWkO34rtf4B24QxNKdal7Fcx\nSfwCiZhXXFFUANMSds4T2fOMvv1BpRfBbH2v8LYWAtpm0ZmNR3WA93cytvaO\nGiMpAWtdF/7aWLYUUxZ5tgSqUHdfIc104Xa6yFM1IgI6tHu9LJ4kTvQUnwYS\nk5xhlFMDWIfp3Hqf6WyJcf91L+Xn5zDkEACP6hMlblhY7Mldncy2XWHB99ex\np3sObGs5a/YJuVHBgEP7EwqrYhyee0oAk1Y0pXVIBAHHzE1JWbOjippwsgPT\nKPxh\r\n=j4De\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDr/yiMUf5WxLUJk+nXu0qG3VFLUwc68HyfUR2dpfKN9AIgCjNyZsPhkgJ0jn/r2D9L6wD/PHx0dFC3X/3n1jV7UG8="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgevjcoc.0_1603011316935_0.0002730096646184066"},"_hasShrinkwrap":false},"1.0.0-canary.kgg7oarz.0":{"name":"plumier","version":"1.0.0-canary.kgg7oarz.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgg7oarz.0+a1d939c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a1d939c5b0bb73a715396ebfba1be722b491e667","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgg7oarz.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-j3hfPjpviXq0TSBILOLX6rLXwrr+blhzha3GyoVU7D5mycedtee1k913SR++ntQm1xlUXHLgauQ1iKW6B2ga1w==","shasum":"6d434c56bd8694ed9581727d764b0ada3e1007c3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgg7oarz.0.tgz","fileCount":13,"unpackedSize":33081,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfjT7JCRA9TVsSAnZWagAAcOYP/0AlGH8ilwWzFDz91eVl\nOe9R1UzSmFm82TCdgYH/E+qBCTryLnl1XqKhdhNcQaOOvGNnCuGopRiKIwlT\n3rJAgUU7li1RkbSYpcrC43g2tGTP7XKHvUqLcFD03NiXmmfEjAaJwbMPAyeS\ni6MEp21S909UjVtOHbmsBxLQrdWSebvnm2yWETT+5sdFNF9dLMJfE14lznx8\nPhr2ULzD9ykfp8b/5zO4EW0KvjYyFKvAC+ON7f3k7mqKjK3JirVoI36Awi3r\n0LEZuSjglEIqUCnyOST6S+s0X+LhITV7Lwn95O6CCuR2sh4gE1cMMpUqE/ER\nDodKc/HpGmotAsTuxYTIWUrJIDr+ulrTdvmPRbUIk4/pTovblVtlgHOb9+hD\nfhANLhM8W40YvvesscLXhARBqT3Q5eHd0/4vXp53/j0CrZBb+nfjls9CDX/L\nyK6iKLOeb+Vu93V/bqNZSxYU2jxNdeEF9b3Q1U/gRZixAQ/tfCEZGA6Le5ww\nWgWt5/8UfPayYc0OAOr66jQwqCYUi0bcPHQLP1mNO6p5nUi8xZySmBIg75KF\n79QqjBWc0zyL1MRcJy9PmfaaZ/r6xn66Z8e1EaI/KNnvuXYhe8gNYcFfqp5O\n2ARWKuQirF45UG+8G4GWvU3X688uZsnPPYdU/fOMN1/XWp6AYuv26/Ifh2AF\nXa+Z\r\n=Xvks\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDx50UHP+XFhP7oYBM5a4T8gkJE6n5vHJY4vPwb3xMmRgIgLSt/9/2XcBTDcJhI4jdd+IV8IK5CuQ4F2GA9dsUH9lo="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgg7oarz.0_1603092169074_0.5316806041749653"},"_hasShrinkwrap":false},"1.0.0-canary.kgm10qxk.0":{"name":"plumier","version":"1.0.0-canary.kgm10qxk.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgm10qxk.0+943ad6a","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"943ad6a5984efacbcf25d99c44cd50d3f601da33","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgm10qxk.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-D11mm5xZJccnPPWi5FKeMKDqpbX6gBpHYZGz8L5XgKmA6CVWRfnIypsYxm0EyHfkQB3RmarOXbz+XUaSrlqmkw==","shasum":"70e102356fb68dd3b2d041110aab7f87e24d23e0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgm10qxk.0.tgz","fileCount":13,"unpackedSize":33081,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfkpxQCRA9TVsSAnZWagAAvL8P/R0d8rghWGc4XHzy2Knb\nJnjd5VE5eOhrDlIWyIxF6+8nKnPj/InGKMJFrHkjk20WghykU2ntuNRMLzTB\n7KMXU9SPbyeGI/NS93wejzmIO7KUvzQGhySgiIjIOE9/93jp3NsOn/JZ3ZP7\nPdqBp0qQAbv9/u2X/WeGdNEWv+hKUhK5Mswxondytj7jUNpRhcXqbcZfBptN\nnr0q5KlnvyltFjl5UCsXyvaGxv8NMDrWZNJzwC6jaxIumWQrEffaSwDRJkSZ\nE/esqC5lYjuB/zbfCEhwbQMxaGQ7fPRSiAY2cxL19aF+fb0YCGwS/wnBhNU/\np6oetzMcozRvTHkfyltX5SK+8kweEY3c0SDdd5J8nqJ7Ri92SgRQ0MTs6XGW\nZWDwaA3DS6956GchJ034xUPxcDgQ74wpNDk0n+/7cNaxqIRdHAVdIem7oNHV\nt28yR+Pkm9olu30xy6Fg/e3NXn18Q5GIc0PCryXVQPU1FrlPRubJ6TvTK0Vu\n9CcUh8BVW104ulxCu8ULTKXQCi64tcUbYk2SGH7p7mLCcPsbDr5Ma5VTgqud\nAzkkIlTmcOv5vnYq2aMiBrL5fxc3Uo60NnzOT6no6/8u7OMwDoBear2r7X0+\nMIztJViSnRT+APnITgiz1nf00wYaMPma9KFmjQNKzljLe/Q4OT5rrZWBwTX9\nMWV/\r\n=YQ+Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCzCk87fIKqEetjoDNLRAwYFvGINmoY+iiOS08EBqE/UgIgZawHWPZ06vJeaEkoTtdCgcXz4/aXnDPLNLdRmKFHLIY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgm10qxk.0_1603443792036_0.1360413251164645"},"_hasShrinkwrap":false},"1.0.0-canary.kgm5iq6j.0":{"name":"plumier","version":"1.0.0-canary.kgm5iq6j.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgm5iq6j.0+92320ef","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"92320ef4f5d93c8cc73a79f7f3b90ee710f67ea9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgm5iq6j.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-lhIyWYmSQhyLeTwXOySbjtcuNZ4QEVv12oCkha7JaVXxlBvTiuqlY/NLdFWKYjjhx144yKrL6JVw8ecHzKuudA==","shasum":"0d3ffc5d50ffcaf757785551809c6f5e6caac5b9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgm5iq6j.0.tgz","fileCount":13,"unpackedSize":33081,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfkrnYCRA9TVsSAnZWagAAQY8P/2LgIyYDxlYedVhJrUC0\nEn5YTmS2Q7VCR+nQOcxlT1p3VPT6N1cOFG4auXXjDtlZY0/OqyuKpfM5Ne/V\n5Tl/5hCk9j2CiZnfxDKdln3tR7qsFGW15OMxJmp1sxBItQsqBcUXMZ6Rty5q\n91M2KPeotf9dq5zdm+RzwA3fpkPBXm6SjxWKLdBN9Yu2ESyqHu4OwegE2uTr\nOCNoKw2tEnSGo317uyAcc1TvE4cH+bq+AZE2YF4X4xMA+RIR3cWt0xs8VpME\ndjSDemwFlXAz7Vw2F48AsedYdK5VWL1CtjZsbvSkoOqnsc1yAkIu0LTv1TEI\n60wuVtBojT2F17OTHIXkLXo7bqSC+YuYw//o6J/uqKSjzPte2jTEvs2kXFYR\nCrRD+rdGu78v9U63ASUzWTytRssXTxdS9plGNPHsGdSw9uJiEzuzjKwEOeSV\n7yNufIW70D18u5WMw91kBv3PO1fN1SIVugFQRbyRN7PwycWU5jvtuTp3k4Ns\n5F+raWSg5W4hZKqpF9mQVlmhPIOXKSp7ZDBwlZUwu2UaNlLGLmktUER9/F9o\nXXrxeLwVku6Ht89kememUEjN/LJIy4nxoIC8uk4M/9OwLgN6rKHya01ZQun1\nJC76+KKIeVHN6Cs9p+SEhhxHpPQ11YXD06VK2HuazmYiGmHlJsUf/PKXUIb0\n7jGC\r\n=T8to\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDK6vjibA1Jkln/JwgVgtIO9+zj68aeys/jw/Gj8leNYAiAwQgxkYYqqF1DdpdUJpHDiJiUKaBQm51Wk9f7+bujH2Q=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgm5iq6j.0_1603451352060_0.20598582168255097"},"_hasShrinkwrap":false},"1.0.0-canary.kgrcn168.0":{"name":"plumier","version":"1.0.0-canary.kgrcn168.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgrcn168.0+6223ca8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6223ca8337aa3db4e18b41465f25314d97d3ab99","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgrcn168.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-b7euWICGdSKUmCCOKJKGkCCsSBrDslpHsXJD0hs3/KeMciM4WsRbf/cTM3F/f97lFypF3vevZyUc3NiNNQ1KNQ==","shasum":"08a9c3546007656ea4e7e981b76cdbce47f1fdd1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgrcn168.0.tgz","fileCount":13,"unpackedSize":34063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfl4WNCRA9TVsSAnZWagAAPjMP/0ctVD/LU+1e2q/vbnjt\nhNnl4NdualGd+TWdp6xL3zk1RDTw+FIN1reHi6e53Wbq4Z5gTl48JHCdOljy\nGyb2d53QPp018AYpg6vNAyDxPomPaaPGbcHsHJ7UpsmNZ1oOttU62Oe0NyDh\nT3AFfi3sl76bHhwUi6NCL/yhkDl5arJtmE30fVZ7MoHHR7vOTZXZqiJtnpu/\nu8ghc3msYJKTrI4Ihv5ReXqUdkTvgV6i8Wql1mPR3/6qPml73IEzvrB4tY3/\nh2fL879dmMHBMPcTrLVF17Oh7sbwaInfO2SODlWrA2PjLkNuaxwQuggOhTIk\n40JkiErysTH517LC5+w4d1dxZZv1Xm1y7CFXtac5GJngdlYSX40ikt0SDu2Y\nQ4EykP9xlX7h2rY7AxRPntTWlsfOHh5B8NFWzRQV/R6scJthf7BDItonDtGe\nmPX9gGe2SuUM7mZDG1BUVl3UOdjo3KEiF5UZAbtGOcS61WS4yIkYFYBIbnb9\ncwh2H6lzQsyEG+14hXgXiCKNQqEE/NtzOMFzT61rCY0x9yD30xjRjkfZEEHD\n0UoMwNAGNN9mFNAxzt8Z9TolN6xlbeZ2tF80iRZkPCy87yRL/tYYfesr6xEz\nV3W98+VCQMNIn7x9t7DubRWaUt4L+3xDfWXNM5Q2/T7VKLyg5pffVY6Vf0C8\nUpYe\r\n=nRUX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHIhygEy/3xvbl2boLqyum/y+7NfgjNuy4wKQDecJH80AiEAhpR3YxU8lEHGPzQg/A26Kc2WJ7CSgvELHkV6PFRC1lw="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgrcn168.0_1603765645016_0.04093041157502797"},"_hasShrinkwrap":false},"1.0.0-canary.kgsjdgwx.0":{"name":"plumier","version":"1.0.0-canary.kgsjdgwx.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgsjdgwx.0+797e401","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"797e4013aa8d16bebff7657b839e8168eae9bc7d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgsjdgwx.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-4ouI5qMshCWozebVBoeVN78Cnrc1MvX7gsrZnIW+KOeuw5ZZ11nOl1Racyj/W6A3MKYbr4mK/VyjFq3g8pW8wg==","shasum":"80a094b6abb88d5d44494da995f3a7a1bd38ca06","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgsjdgwx.0.tgz","fileCount":13,"unpackedSize":34063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfmJ3uCRA9TVsSAnZWagAAHNoP/jyCHUUC29m9uieQTiFs\n1JbiHvyplbLb0tT9/n0MrRHF5cSZa83VuWe3b2aZ/Gq5kwDl71VeNOIoOQjU\nuMRXWLn+iMumMpVkiyzU0dgPw2Bpb0uYRU4Btp8OfoPud5TMrPNSAFjStuTy\nNg8vjacAZxISb3CCapEdT/u9OqgrJLCDDCvPCOyKFzhyrmO9DZKaGRXLvags\nFSXOLjJKYUUDij38ijAMFCdGqkHmVqcz4w5S/4wtfGMF03d8bzovtXQsk2zm\nvfUEq3cBBI87B5sjb6B18AVedk+8nIh9As/nW2O1eXkst6/aJ/csvDa65uZ2\naoMXUp8R4EUxg6lRsHuOzToKI1yJVyfTSId/IqEL3jbtm9Mv3+WpjbyWQmOK\ny782hhtTZwFz/RrioWiZ2JEL1ItDrKVuAaWRv7Qc1InGTJMlfZ8meoiVhApg\ne6dg3+2uTxUwoMKL6SluUVatny9GiNdLYNW7ZVlkUR+BfXOM1NqFVxJQdiWV\nKzhfMLuwn2OaR2MFgjFsFl4/h82RAovvRYGK0AEmWCfAB0RafVsES3mSrlwh\nWTwsdGXy2ZLAFgbANBOoCxUqGPX5hvfuZMCbTLGl5KtWwlMW749Ij/kgAsHp\nVyG54sPJixzO9o+5M9BBZF5B/9+G6IT/g1fJTq01GXgyhZ++aTscqU2l6m7t\n0y77\r\n=j9zR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBuJG8PPLz6/vBgWls6Ol0CJRij8gvxkEPMJ/GZnmOlLAiArF+yXHND5/2E8tKRxcy4YAX65TgshTA3KI8TPADtzew=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgsjdgwx.0_1603837422120_0.9935797332037624"},"_hasShrinkwrap":false},"1.0.0-canary.kgsps60c.0":{"name":"plumier","version":"1.0.0-canary.kgsps60c.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgsps60c.0+b543fc8","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b543fc84949ac0ef83fe12db1def8949f78a8590","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgsps60c.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-iEMcBptxMYQfhMUy6YFFrH3/SStf0IEtEx8WH9yTZhFeYJH0d/GWgNv2NMrj0qczufuNeOcLGUxbmpU3a3efrw==","shasum":"6e150afb5179c91a3bfe95ec9062e1b9da364ebc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgsps60c.0.tgz","fileCount":13,"unpackedSize":34063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfmMf0CRA9TVsSAnZWagAAbyIP/2bYflPTntgloLlELR8p\ny6UaT/FXtW/gAb+q6Mic5qTCm2Vkq2e07STVC8QZ9bGKKkJK1ywujORjY+CU\nHuqJXAiiomW4lQtUQ213tisF6FWlqptIwSlRCne5iZEk3WJdXJg+MHR2Iapd\nvaEqdIEt/bJ5vEGI8Ahh4T/P3lfOezoYRFStHSJ6Uwj8SYbzfTMGnwxod/cp\nwqXPD+PjXXv9uSIoT9anfmKYUVJFDjlMEEV9nMu6NQ4UwahNqHuRr2E/qa3/\nRAJoyDY/v0KQAiX5Wo4QqDBNNPH7c6w6NxLsdXsegpnzOiap1UCtm5BFCRRo\nePn/cYg+/BzWwLq7E9S+VQw49ZrvwWKJJaIb3mzYvd4yYs0PLQBr+pMANGrA\nBpTFRRiKGLTaexzVui/EQ9XO84dRJbLY2bLfCdURInwwVVc6RharB+5pfp5N\nj8J5bFXNh2Pk1CiySligfwIE1AakSCjNl4hbFjvWaLka9z8bXIiU/Xe0CqJ1\nmCDd4tGQ8j8HDfjm8PCYosXevO1cC5fZSfCjGWpNiietbfMfqzUS8XQoxG9e\n02kVksYcjgjmYbFr018bLke5nYJYcWsV0T110JMvsRbRneSXLyolJmxcSYqA\nMkiPu3UmSJay9UI2FYe/c6MhGh3MdM3t7mQKIOw1vtcOsgPaF4U5ETp0hChL\nf33y\r\n=q2bF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDxlScb0iqEH0apZMmW4ISTyPMC/LL67fL+810sGZHWDAiBpdrGDVJfTOB2SvB02lvBSkMss30ZrQxLp6rWXRE+g5w=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgsps60c.0_1603848179592_0.5030094192777559"},"_hasShrinkwrap":false},"1.0.0-canary.kgxa6f2k.0":{"name":"plumier","version":"1.0.0-canary.kgxa6f2k.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgxa6f2k.0+ec70d4c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ec70d4caf5f05b7fd9710de0162b7809538d6627","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgxa6f2k.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-+Ogq5MSbgWY4lNJECFo7G90lMGJEPWEhjC720IaR22nxwim9wiuwBginqpZtOQZBoPtZZG05kQoPRFgkJ7CYFA==","shasum":"6dd68bacd1cc1744aef69e3c85d0da0bd344e949","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgxa6f2k.0.tgz","fileCount":13,"unpackedSize":34063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfnP6RCRA9TVsSAnZWagAAajgP/1l0/J0uap6zv/3BISA8\nBNCBO4T1e58K3XSCrZEpYnG+SqBNi7y0FJtOyf+1BcTfRDybXrwiB7YTqJAt\nh9P5i3dhX6smdWrxvtVWtiYb/6WwjRGCn1NF74mW/0Rv+tSWnr0kYaEMfQP8\nJ8dmO5duu//hF7DwSxmjktUp6qJWS+/FtgNW527yXlY8Wf+Pliee57Och/q0\n4Z1rn0Q451nXlMTGrQGydrmkwKLS2h9FU7x1ioEffUuqE985lnnSDCwhO5p5\npGvs1vJwrQmsLhaqUIxghy6lo5h0KFFA/6czMP3MoxGRM4KHh6vCdLzSnPbf\nU/nxIm3xgv/sU+wbO+Nfnt0pOleCpuL81hezklf0hgzEpG7CdIA3wVEtb3aF\nQa8jkuXFg0cg6GTaopGs7h5nmbgt7rmtKNv3xsiCIP/nT+Elrcjgx18mKNGS\niM90bksS6kk0X4EcGV25Rwbvma0kvSADEmYxwNs17DLfXKNEjIyykW9+nIoG\npD5XmauIrJCovOKdwWg3LpEYi3a3rbAZXvpkkMHiknBvRPxWkWDKl4HFRez/\nUk/y2jg7lWGH+74jZxcM3Hb89xAdoiN5QZR/t6GG1tymlRyAfLweMMbX73Xl\nDUjc8sI/4b8h4ZFWaRmkDqd4tNa8qNdo70f+RjjwC3MgzEBDfTkohGTqCgM/\n6hdO\r\n=tmP/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC6lOeiUzx+qPdLbKvzXff9koe+RJ5/LCUwkkDAaFWT6AiEAhhtHAwMEz5HNNLeCghg6X1PI2t26CSQqZ7Ouo6AvT0g="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgxa6f2k.0_1604124304885_0.9503504813531245"},"_hasShrinkwrap":false},"1.0.0-canary.kgxnbu9k.0":{"name":"plumier","version":"1.0.0-canary.kgxnbu9k.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgxnbu9k.0+73c2aab","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"73c2aabfb053333477af3c6d26ea8171c1f955f3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgxnbu9k.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-12his1YsneEkNSBGkGmy5NJU3FHKEKPH3iUJUSo8usZSeQELrdkYf4gxSlPG0pchP29OBGprc9EFABKVCIttdQ==","shasum":"543d1147398b654b11d45e7687d3c878fdc1c635","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgxnbu9k.0.tgz","fileCount":13,"unpackedSize":34063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfnVTXCRA9TVsSAnZWagAAPcgP/Rtw7ReAOdF3KvUlneFK\nQ4DWoUVKArbiXH6LQuEcJVev1zlk9rBLgHI4/ZBkPasdmxHq/XJDX5dl5r+T\ntUyewS3Xnv8fTm9zbpy6R+yU7uaTOQFvn+k+QRR0yf5dL2YWDva8BQYOTjs/\nv83YXdccVTBfsfDJ/x31lflxttsQLLCMC9gvKbybYB0PGGL3qeeR8TdCdhni\nYvRuqhV7L4xSBDuWcKeO0pj4TiWBl9vJCBq3RbEABEwGrtIoKipLWn9YE3MM\nM+ea3gsZmZzsZKvq1e3hvWSH1nDNlVmVnBWMee6SLf4TCZWmcgHVsrmRDQNS\nmff1Y9cJhwe8VW1ftRszpY/vr7mMHYFd6w8MiFv8sjRrzdlhZaKd4Pu33msD\nvEEYlneENKDXM/0zbPf1Y9q6BiFSFLXlp6AVOa8KCXHdZ8qbfxSrYhtn2I/B\nhcg6Zbaz9omo3IbrV1pd/d5sW1rSwjUtT8VA+4pNWpu3o4ke0Zdd33+caBma\nrODZk52oOeB3KwhUfeN39RdO6OysSK+efbE6NmF/kta74ux4e+CK+dZl+TDr\n20CBpdBJJ/FDvGkxisSIVVLxHW4ZCO+/fLMa6AWxxX7e0FpE6dhBwiD3TFwk\nHGDZqLAtpIQBChalgQXbnkrQ9Rt6Z3yW9QIhXC1tn3uB8knN3OYPBlnmRTRM\nVbX8\r\n=/wmo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwQ6t6PLzwm8fxMR2b9GIiJKhy6YCDDDergwhMGPdFhwIhALrMV+KAsGVvVrPPwYJ+jsm2IfozJOgdr0/GYtkZ/NbK"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgxnbu9k.0_1604146391208_0.349049786559833"},"_hasShrinkwrap":false},"1.0.0-canary.kgy8ctsk.0":{"name":"plumier","version":"1.0.0-canary.kgy8ctsk.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgy8ctsk.0+da37e0a","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"da37e0a1f6e349e180229c197a0904162774dd15","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgy8ctsk.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-fzGMSwxfjfrgh4xnDAaVfoy/6YQX5oxSPtvtVZxPfsNZyqCAThMqXl2Jp3Z8Lj8E5UhsgUqHOFFIoI7mYHehBQ==","shasum":"3fb5c901587c9d9bfdff58e44f388130f1c7d023","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgy8ctsk.0.tgz","fileCount":13,"unpackedSize":34063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfnd7ICRA9TVsSAnZWagAAClMP/AkNiS9RR3YDCInkkutV\nO1vlchm56XhFkWH/Exn6yKgvEp1MNzvTqBsNE+ElnMMp0dWwEP3bobNXsvbx\nCravTmGdC1eSEIyx2LMR+tVhEy0X3cQQnCx+xYk/3W1IGnp4rpb2XwWNI/Yd\naRychLgLqDcM23haaHXJEQIt3TMEq6ZYU0edmDOEgKUmrcdcc+wVpmbYcZXS\n54fLrrEUlk/vpWRalkIWgvZsRcw9X2hic8/KANYx9g+0TwkBe32hta3NGVTl\n4jrmUrG/pbFX3AGvaTVjKo6scRopReXjRPwYCaygJiEDUyNgzIylcfDmQIh3\nKk6/cu1fC2X0q0NvlU45faJTFq6BCOW08LiYNVFB4p5YobnLHOVIPUFvDg1Y\nUSqep5WJC8A9BimiaILPbKilBpoHnJMZ5qSY4tLjKBADqZeg2tvg0UbkphgS\n0hxDxOYxHwaVEqxeAV4rLvrtPh8Tgb1iTf2yu6f4DzJ2OPuXchlpx4WgyD5T\nND52VUoJ4Aom9PjUjNi4TSVxCBhhRR9UAN48Bg4VcyEsOcpqsECYZqLHbUo1\nyj+/9/ZASXXv2Sz6s3beFM+lV8K0szPAZ6MEBAnTV9LVIfUhCQyT25AdRsCV\nMd3yElugKlZ85Kwhg34PJB8DjSGB/jeRKbEbIRKXeUpAPVVf3mOcAGtpvSKu\n7OvG\r\n=iTYr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCNN5qZQRVIBHl+rTQ1A87Bl4WH/d2MKGVym3P9swpfWQIhANO8bT4ymRx1Zfkk9A8kzlYYOQHa56gw8OyTIZHPpxJe"}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgy8ctsk.0_1604181703788_0.7451026013902295"},"_hasShrinkwrap":false},"1.0.0-canary.kgyal5xs.0":{"name":"plumier","version":"1.0.0-canary.kgyal5xs.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgyal5xs.0+56ad51f","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"56ad51f6d19f7d1e4667ec77d0fa896af12cf68a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgyal5xs.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-AsfOENd7KjZgHkeOEh1pQrmoyLoDtVTyfObu+tCYqbuo4K5887nx/0ZqCw2qyAnqcsImYvF1qJdiMzeVnEkUIw==","shasum":"89233a9d8734192c47c9fb58b36cf79dc1fac621","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgyal5xs.0.tgz","fileCount":13,"unpackedSize":34063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfne1tCRA9TVsSAnZWagAAuekP/iB+/YoU41Ga+PTP4pqB\nNSgLB7ZAaGRyfyi1rMAx3/dQMZiydYIw3gU9E+Dd+kFoaSg6ttE8zc7wkX4A\ngPwDXQLAscg6+KEJ0sWTDtfy5brFMzAu0UGBfHmd57Q94pq+UaSPVn3cldXx\norJ3tgvnxTbexB1vskUN/CNltRqDHlj3y2nNMPUiSFpxchUy8GRwszau/8Jx\nCzuBrD2vThUaAKv7XsDTC/m0tAgJ7NMKSBx6XKTWAJ1HQShb2T/Ybcvfhj9b\nHwaDVuTkmB8gSTF2xhTbnIGi1sdalLqjJhvIK1iYkNPCUQNYWn3NdjtZTrU6\n2XC8qT1tp8VBmirAqkRmC9ixLxZES6I49+B0DQzGRiXaBZdNKynO9+2LO1oi\ntKFAd8/pDe14ipkS0yc4vXVOMkNTfA4dAdm/N5XilVdk4Wh0QtpvrnFycXoZ\n6ArgPGCLxqbj6K9m/6pBVSEGT4C8u/zjzPOIETAqTnEIKXof/INZHGodH17T\n6ta7uzfhtRJKf8sLCKwNUdzXJCQFoHGbdRYJcWphAdr6jpv0b4gMZ2fQxziI\nNQZBhlie1wsyMNXPAHzFnVnbEnrYe45vdck/zecXWkWMNr+t3tFwEo7n3+bR\n9VYVGlS8UGEuwLZDawxFhZUqlKHAvlDbb2I5uIwReAwfzeKORI9GEBu0DEuu\nV7Dg\r\n=K6Jm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH54P+UunaIeSz2bEtwMZRu4u+unjk4NbrowR+PD4nCuAiEA3m9pW33ADsdWeR1cIG6nx696gBQJbh27/clDm3iILCM="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgyal5xs.0_1604185453030_0.4744593869603462"},"_hasShrinkwrap":false},"1.0.0-canary.kgydir3d.0":{"name":"plumier","version":"1.0.0-canary.kgydir3d.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgydir3d.0+567ece6","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"567ece6c1645980648f6c33eefdc2d7b6bfe6d7f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgydir3d.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-pw0Ygdd0UnDP4q2iY5PFbqqE7d1I9TcmjV6//DgO/Cs2EGKQZzOYxM5ndda2W3OswbTxhxofmJZQf7zJtq8JlA==","shasum":"3b58e2c5e217771ee2c1d00fad64fb5553a2b003","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgydir3d.0.tgz","fileCount":13,"unpackedSize":34063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfngCtCRA9TVsSAnZWagAAsMIP/1YYdQplvxS4nfHVSuRg\nqDPEByGLEasRiHwWhTJsld1HyWjxWKks7GrZ15hzIotmKDWG/BqO0S1nj6IR\nozOt3xoNq/ATNBiFxzYEGdElBqIpsTQtMg2QHIsjLUriK29Jfzl4yeOpEXEC\nV4cwFWI6wCS6J9L/1FrBgKgWwMefJ6ELo2gUuKXzQcrIRaCQEviiFb9v0trA\nmu4mkJvEKHZCL4kMsoSjfnEuKyQgMH4MxPjPn3wVT6Iymq365Xq8VXYLQ6pr\nkhvzZpeOx1GkJxSIvYnAW49wSYUxutGZ6VU6WPYxCvom/MRtVuDfE9Cvc7cj\nBlba1tg+VpZC89ZbS7iXL2SQFlXhfmzTmDzUaJnKKTkhEAi5elIz2FXqWwXp\nYhwBhRoNtj88Z1prE7oeN+I1vfDC837qDd2bNrVqZsYXC1cHx9jdisJc1uGh\nd18bSPpaRyWmSoWanXSYIw8eAln8fQHhwUAfcCtfTmdkx7IezjcNcnfSb06d\nmPYGFTsyzvtPajX2dlvqEMFwJ9uKB+5wR3CsygHtwJh1iIRq3ayiZksCTCPD\nesg7Ci8jJo2HpDRw9BcBqm6UluYbIojZnwKFSYjUtCFHJTFLMUVVEp4V7Gjh\ntbGpr2lwjBcze5CBbbNmpNvpFcCCfL/TPa/eNKGRGNkaiiTvmy5Vwo1/p1Fv\nhWyH\r\n=spaI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEIt9dXCdFYisTyoY9t42gWhldSknV30Zd/ZkqxlxE93AiB3MsRRS7D4QSOThZOqoQzPJUgtmxpzF+wRHeQyd1Xg9Q=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgydir3d.0_1604190380851_0.20067790580970568"},"_hasShrinkwrap":false},"1.0.0-canary.kgzo7o56.0":{"name":"plumier","version":"1.0.0-canary.kgzo7o56.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgzo7o56.0+b39b189","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b39b18963ee9792f9d49d79a6d9c17e80c586b9e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgzo7o56.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-DwgtrWJrEDpRaPwpLV9x8eghE73aEknHZn309fpPImM1TzDzHPfXBkXEm5iibx3ydEFNg9D5EE8yks7JRSr44A==","shasum":"e3287278b8054376fd7ab1ce41332c1c43403993","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgzo7o56.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfnzMICRA9TVsSAnZWagAA0uQP/0W++dRkZCKD7cc/2fM5\nVQvSi5fPYGnpQCPB1BXYeHD2kl5rpDOopw17YU3r76FW4iQVe4odzcRnTB3F\nYrqD+wzgmc0R+4duYlWdBL5DGtOEX6tFZX4Pg2T6fUOIcTgKX3MVdzk7qD5L\nA09c0l/bkKFMiITMIyh+K0+i24RYN3uJ2LtGREeI4VXTgKscvuBQBdJbE62W\nFOPQ2BeCMJf4dPdbHsK08ELfnd3aNwLUMVqmgatFilzlZvHSzjLI+3SjguvG\n2LDkQKO6qzQIdf9M+Zudtqi5F+MwRI90DeEM96GfU6k44Z2P3phI6I7cc6ig\ncmDaeVv/sEYrWB2W7yP0MkBMTZuQG92FDWmPh5VMhBoBVQw5+sEWhl45BeK3\nxwBEt09zGZBCxL3YWwYm9zJtIgEr7Nk9gDqFLGb2zJHZJi7C0AIHKsMMuLkq\nCZTVSOxzDWPVTDKN2FjuXrOmViu5LAvUhWo1S1B6XrpGOr41zSDSSsPxeMSL\nb9qhNGygp2OU+OHIR8/MBhME/DqLkmdbkYvJak7cMzISMWG15fi4mr9+G2il\nFCdH4VWw879C3TIxnkxXBRVd0m5Zer0vwBvzs7UXAO49Eyz3Pyk6CGuXXwBP\nfRs1eXzbsVh7g1doKT5umg0sgwhEiSexX93G2i8IO9IZ7YBYcojDlQ5zEO6o\njOP6\r\n=MVgK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEW9FXhxgcGQCZ2bkY3fgJvmZFxfZlokK7tYWIH7IZ89AiA/mMNH4xO48DMW7mRKmA3QWVg9LAatK+jVPrmJ5eEweA=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgzo7o56.0_1604268807555_0.5561810709970283"},"_hasShrinkwrap":false},"1.0.0-canary.kgzuclth.0":{"name":"plumier","version":"1.0.0-canary.kgzuclth.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgzuclth.0+d74a0e3","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d74a0e3e7c0520483ff308575cd4f52ae7ceab7d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgzuclth.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-WXBbVnXVkdm6bOVRUsEBSRoN//9q+ICia0YxFP2RUOA/q71UInfZaugkCDq6mtPWpYuKUR6Ca6nbHNu+QOSKdw==","shasum":"b385bf3aecd8c348d8809c9c6dc627ff6464840f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgzuclth.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfn1tHCRA9TVsSAnZWagAAgdkP/3fjQAdF4+l3OGbII6YH\nV6jtg1vMoTfBAEFiFN84ZuDeeew1r0q7xEHrwBJ4I//CtlCKruwarOrBDZ86\nXFNNk/fpCeWBdatVChK/qXuiyPEyw0+1XIl9HUfa/MyIQK4e4dGIIXxl5Ck3\n82fA2bmNzlHSxqh0ezdUihgmLS1WPynoTHTqaCfnLGWMpOGvJDkC9oVcuEPR\njW8uL9eAi34Y7d7SThy6TjIyw82ydik61VddMxhwME8ZfXjeaNcNPQeET7iE\n8Iqhg5NlFmDxk15HJmSUh5yIMEtWyq3b+Hy1v8dyq2rNu/d52zSgPvuNEjI0\nkZD1nseBt05EZWh4G6Wh/3odU5rMgF46JgBcOv9kfM4dwjOCYVn2QHVBadbq\npd0irQIa9vIlvNCJ31jjMJCtm4e5/RGN/Sm9zMlOLIrLOjSLtQoxPOJYnEfW\nvKwEhGio79/q1i+pPNQv3GUeKkiROT3sE/2yY57jJLqbp3YnwEJtOiRr6CpG\nS0Hy/Zx5vqlNqHJtghnHMxTWoafvrjBTJLwTM+fJCQ5FmFROdpfBsK9vTWYM\ntDQeD35NwF5CVpUGM3Q5lVSiCALUcCEk0/QTrmtj0B04BXugrmPjDjJjwP0F\n729Pm3KkIWOEdfz5I17gUOnuENb0xT0Vb2dEgYRMk9D9KH8Bqis4MQG5HtMW\nrBV2\r\n=rzPo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3iHngqHf2/3x+yUfF6NmC14meRRR7nMWs5UmxE8SxuwIgWTJ8073MMFUjy/Spyue7lCJFuUnHXIrUVC/aluSlhYY="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgzuclth.0_1604279111387_0.44445034857953525"},"_hasShrinkwrap":false},"1.0.0-canary.kgzwhr7g.0":{"name":"plumier","version":"1.0.0-canary.kgzwhr7g.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kgzwhr7g.0+4e753b2","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4e753b2fc3683e8d19575fd82a56068ac9b393b1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kgzwhr7g.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-+/1PgIYtTuFQTz+1BpqC9Q8Kae5iqreis3sV+OmEJoeOVrnWe2/PSOzCv/dcvD7G6J317Qz90R/fBREYMbWSeg==","shasum":"4d313e32c7eef5775e4216b9e0e46f935c01b8a7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kgzwhr7g.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfn2lWCRA9TVsSAnZWagAAsKUP/igC7fy0M6+6f2rTv73t\nxUfskmMQi4pgcj59LLcx35cMF50a09CSCi8cKFmNxTu7GaUBVIOsh2uwk4YC\n2MN0rpDVyzd14w+fS897gvuj2JR/PgOWbRsdfDULTVJxswm/Zx6vx+if3j9H\nYg718z0MM961U4iamPSjLF4Du/12haC0UasQygArgtzTaBO6Q6fkLFDIV8Y9\n+OXKLebA7kcFu/y1rMQAfcsH0L24esnIlFCLmZiV91rSw/nqlqwOoGAB63SP\nVRbPwJexPBt+7FkvOyCA8q523O/Bef++P5/fb4ydphyhmJDLYwZkDhaKagmY\nPm9m6d5qBPWGI33857ypd3Wp+Kbj+A2veKYbKtflGnbnz22tVFMf8buUEt4s\noT1nTi7eib44xlIxVOTx16LE8J3uy6kBlRxRODJq2vcjX6Meh0kNVGJVHQai\nNiHoze+81OcpcEc637lAB8hKb5Hd9Ru0rpWo8gvGSNIyuL7Cd80k2CO+PtfP\ntOsvlwTvLMuR8ZmtDUNJaCHKKeNy7cq0n3ko2qpy0Pee0j4VU9uNKw9wGdpQ\njp5rFXZ+ty50tCeGx3EYPvxP/W84E9KQ9jnZDawV2Y86MRwxrjGQKbDL6ygs\nAEu/0uWGlFIb9u43ZsZv0ra4kRHpSVRgUCYxEisYV+Fbpfm86KDfURlV/f85\nZuSI\r\n=5E+b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBQSpkkZULmos3rKUW2ffzf0g2ztZS3LYegJSvK/YW+LAiEA+OwGt89vcHV2JJq8E/8GDOlrGw4EYhpihN+x1qMjZ28="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kgzwhr7g.0_1604282710488_0.4844373714122565"},"_hasShrinkwrap":false},"1.0.0-canary.kh1h0fly.0":{"name":"plumier","version":"1.0.0-canary.kh1h0fly.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh1h0fly.0+1d1a670","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1d1a6708a612c13408919170cd80c0fea18f9986","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh1h0fly.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-pr98e+uGn8zl9hwFBN5VMD0Vmb7Vdiwz4m26zqgYXCauLQamyJy28CXlit6bpf3eFMmJyXILdL5pMRl9tUQnJQ==","shasum":"989ad6ab9d253c9e8d16c652b6ef24e32ed4cce3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh1h0fly.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfoNwrCRA9TVsSAnZWagAAv/EP/iUyH530XytFYG0ul90F\nytdq7wGOvUIoKYEeAQInDDyXJhr8SwUarSdD4qVunAJIxkst2tHBr+XXug3H\nel05ONJ5bCtqsHcw+7ZSeIT8oLHFRRoKgIkvbjZ0dAgiwNQuQR+VjlMSYncP\nGpQolnxy9QOohACcGB8XEDDUSf+D/8ktbPmKZCWYf+Zk69atXrzeu3TjRKFP\nAetzShx0zwsidE8qkenOScxXna0sXYEgzJ4ktkigNZAAPGltKwVSe2KhaeKf\ncPbZiBWls+BjtAqnYzPZ0o6qs9FzOafrdi36UcrWsU+sgnyixziL4Tx1Q36B\nlL+WMSaJLEt6uN/xLq7PrOtWgClftkWarMUKhZlPQkVumgI8b8JFulc7ZcQr\nq+arFsVOHhkxVq0aZJDUdtdWm2VODrF9Guo1B27YAxeYQbIDXFx0h9Jxwx1J\nmRjxGDpPqVttQSPn3rztrtci32WMBzgu16PPCdocIlS0x3NkjIBkOpAXWRX0\nevq9Mj3IMez/4mJ0XOutOmq0CtO3cSKk0PwjCgVGdqvMPL8pR/Z03Y1/IL1/\nlpII//vPB4D73U6FNXi8JNVEY5JEW7qZKqQrCmbBkk9b7ePA4KAYyoajELW8\nCl9Zang1ApSeOUPz59/cgVG1wd8PClp2vwwzfdvQASaOlVJFM+5st7KztuYT\nnN/s\r\n=MN5l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqoct9MtAGhk7l1f1r8NfMciHboHEyyHM5+twSoJu3/QIgJQNmYnrWAGyDA4tc0AEq+PF8ocYIjlJsuvkbKeVfb4s="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh1h0fly.0_1604377642969_0.19317123804470726"},"_hasShrinkwrap":false},"1.0.0-canary.kh1tdkjc.0":{"name":"plumier","version":"1.0.0-canary.kh1tdkjc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh1tdkjc.0+77ff395","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"77ff395adf798534a276ecbb01278994654d8207","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh1tdkjc.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-1zKnH2FfFnAiPXgD9MGxsKC239D02oRt+sLDx2TWSvSzli4WrUBm93GoYiKHliCcjXZgDoBxWxIEYuugwCceGA==","shasum":"c6ab24141c44ab1c8a3d6aa342f459e010e6aaee","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh1tdkjc.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfoS1NCRA9TVsSAnZWagAAsB4P/iy6AZECMS7eE0OhB7tT\nToI6PlBTG76ZXsUT8P3kbxEGT396KDtNlygCFRi6KyJ6lWMnqdXUv0KMFbk7\nlwJSrcbhoSUoyV5TDnuc4n3d/i49GIJ9lpJqabXmr1crMJ6zBD1X3VH2AkFk\nl2DiCqdPh3YypSYR7q6v8H9Vdyy3MVdPLjXI4BdnIxkQJzL4EkrXOllipVAS\nxVp0xcmeo7sCMs1iygl5VauceAMYTJm7Q5p2ODI2vJoY1IPbTySnu/0t131O\nrWpwaz8/xnd9VFAdi+hcKo1/1fAnpxV55mKUElu8+awSpBJaaONxITz+rwn8\nVBbIxblb9th5iKgcimzkcDi03L4FwgmgXUj6UjgGRIF0lzn/wIyoH+fdWXjf\nzPbWNPT9CHsdRYFaptH7P+P5fZeGbJkf4x2bVMOXBB1o4UYBG2q8WUbOc13u\ncvipxgAj9AY+o5LCJraLvbwseqbrvUaT2X7kUWaMMpQCyP6/eJzVut4klm77\navq2biXHn1j6XCRxwJuboHiDRZoC07vFK97BDlRZ35TlfVLJsak7eUsmysM9\nWayNfbykoMO9heYHOA9jOrzCIcdC/syIg8MddOp4xjw7T1+zw4Pe0Tb8hRYj\ngFUGKTGghKor+YysGaNhPsZFJeX/czbQBvw06RRy+ANlnSYHhqDPHQzjzxQL\ngG6e\r\n=somM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHR+swvDwkCnkx4Gk/TJcTYTZpipBDNdJsLpG9pisR7eAiAyLRXKDjd8jRI7NE11OHd29/yq8KS170juEcqyigxuSQ=="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh1tdkjc.0_1604398412814_0.18936431417772415"},"_hasShrinkwrap":false},"1.0.0-canary.kh1ut1x4.0":{"name":"plumier","version":"1.0.0-canary.kh1ut1x4.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh1ut1x4.0+de299dc","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"de299dc23d09f530b930fc3772f08ade0ea01662","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh1ut1x4.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-qWJpXLuBtLgFbfUtZoUXwkKvnpbOMUOWuRtIAlyeN414prdK0BIlVe7QtBgD0S3EAsI6/MuV/67JBAZ/zprkiw==","shasum":"0ea98e9b60564610b74fab116901664e41be009b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh1ut1x4.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfoTauCRA9TVsSAnZWagAApOsQAIZa0d49BgFrl1Itq3w6\ndYigIrYmFKHP6rGobvEvzcptNh87rBmDymxP6JuQi3QnkVQ5AjL/m9SBWElm\nNUYAqB9pzh6zj4C20RoGxnFnvu02CB1vmxYlcMGc3UtC0JLSXfiI1VF+Ha6P\nNpK5wXeHkO0emvZq500Q8sadLC/YDPeOVq5onU30FqHnePy/lfRceLAdTDgg\naa13VER4TOLQAfdlhvLw+U23EGg7AG3JzD6OQ4sPb57gnthBknsP/jk937al\ns9e1Bx8Ga1TxpXFq2NBkqrKjURXu9KbyHjS7ZGEQ4UNeFidl32YStsAH5AAP\nMVcSeFekeNIGuMd59wW4BygdruagFU1onxW1Kf6KzLjzkgDDDXs7V0SsdgUG\n4rf98AYwZAIUCKkSwl1lELFjOag7Ijf9DXx8dKWo+PgQGKtF4x5If++sHOdZ\n0d2iSxEWgppIOYaZR/3mmk0vP+j5GHXvw1c0qy+8aR/RmWTCieUhjqa7pYO8\nfUnTXC26DlYD8YuJTcYKHNaDEV+CzeR5VUBLhFoEwQ7VefYtQ8wle4aYl2lC\nTCgdVkGyCsRX/rdtMX0F/KyEY8AsKiP9SMwxsFigtAYp2qd5kvftuyDlGyK/\nJ51EwhnaKNdIaFtcYab+NtQzLNRq5/+yQWjGrdEsb9hyJrSOdYF7UmOeg46X\nD0tV\r\n=Wn4N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCl86iVpL2EMchp2pxoaHbw5cTzMZTg8sXGDwR57+STdwIgIkeDQ16V4JBU3Zalj2oF9slgAyuc0JoPeMgFTPOW14Y="}]},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh1ut1x4.0_1604400813853_0.6414571926600485"},"_hasShrinkwrap":false},"1.0.0-canary.kh2ho8dd.0":{"name":"plumier","version":"1.0.0-canary.kh2ho8dd.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh2ho8dd.0+35ecd98","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"35ecd980f098c9ecdc188471b7446d844a2125a7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh2ho8dd.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-NRNSpd+IVWKt+hXchg8B3SaTrJzUbtPCoUqqgZ6AyYYsYSXZIVoZKk3fFPgzWHJMzsMUS6IZ/nhPDmLI9lHkCw==","shasum":"5eada41cbc2bfb994be81c606017f106e6be0f3e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh2ho8dd.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfocyyCRA9TVsSAnZWagAA5uoP/1/fmJNHezU4nQLt/Jg/\nbkcPMr7TChKVjykAhwvYAzSH5vMSabcjXFa933Dpf275wV4KNg++zaqX4apM\nxqiqgjxg6yK7Wy5D2ntWDsocEIycw9LB2t1TyKzLh8YFwipdityJX5eUKxQ8\n4ulerpqDei4bxfb5M75W8rb2Ew6wOKdq06o9fN6T2qEEd91by65wLNnzDGUu\niCpIanKfq/oKzqr7zFGXnLebihdl0Weq4bCI7rD0juWFUukdZ191LtPYpDLj\njJWNne/OIQwxmkb+gFrcKS6RtOItLa7K9jGlkvVuq4QeePgWRh7hFhDGHSvy\nITQXMK6D2c4H9MrwDmibfziAlfNRSQUOig3f2Z9E9p4hF+EPOYiGNEXB4t9t\nwh4XywbO43lnRX/uR1Epeld2yyvCGsEuOfU/J1epuUbt17MXsFNUrYMEXAWL\nygXCxuAbeLlYIM+T2v53a4HMA88C0FEQtBEN6ESfQdwhSWlRl7Lf5zniyTkK\nv57Jr296LjLOEbSdwS3Xrp8H6FJngTQOgs9UWWFkdHLPM9gsfdflqr8xGdV/\nfyZLhhIons83trSbr1sgm2+/Rrn5Ar+YPMEXZlbXpoRdOgx16XxSsgOEcYJb\n2D83jnP5lHkHm4rZjptvm8pHqjTxzDTN6WWmBZWhVWdZA/gJu6k0tLJ7KAWq\nhlbN\r\n=Uvq3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDkxnh4Ca86MNCRaAM5SZLxLE5PEUrTOS3UQxnvTjboDQIhAJ56yA2HJoaEy51PISz6aQ7Kx6zNlO6di89mDLaJ+1iL"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh2ho8dd.0_1604439217736_0.7918702411416416"},"_hasShrinkwrap":false},"1.0.0-canary.kh2j3gf1.0":{"name":"plumier","version":"1.0.0-canary.kh2j3gf1.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh2j3gf1.0+9eb68fe","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9eb68fe3759d21dca66a9120dd83df976fbcdc78","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh2j3gf1.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-m/WWoMnr87XGGdrTmGXWFZXJxQypZiXafvSLOZrav9XmvKmOzDK4mjUMukO89hYXntIeYHqoeQSL1mKLju05sA==","shasum":"e1cfb91009d808e9c2798dae38a43737dd3cd542","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh2j3gf1.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfodYKCRA9TVsSAnZWagAAu+sP+wUAzXWs8Nd9lr2wgbv8\n9zhS1/4U8+U8bv5sk4lcsYkwy2zowy1JtPcfSd3638olAa7KL9iTpHI9zx+X\nF4v8LJz7N0XhtHYQDfsU9sIshz9mD8/PIwN+WAmaUbYXUowU4cL0EyVOC6kp\nf0YdgQxlMD/fhsFoadWmSienvMSvE3EUIHLOXMRTakk2lBDV5biHgBCpCB56\nqv6nHjl+AcjUkI7xM8qkTWTQKvELxC0mkN/vDdc2vdcrWL+r60PWMLiJym+L\nCJLHQcX9OqEuTcZ9q5hIEz1H0Dz3r7aCqz0QzAiM2IF3k2yqbjxaZ8M7uQPs\nZMVgwm5FvlJDWmoDSJk28feu6W4DvMrkUBvGww0qotTNhOuZoHZPVhrdvGQU\n3HapfJ7/SUjkgTBv597qT/oSMfACZ+AyA8YejgynUEQpx7UjPP93iAoK16HD\nmYqL5pJ+D4uoVItHAoDsrYMVPlw8eQWiqEeJuueqZevVRERQCEklugMAKBUO\nk3bDjtfmf9dfkBHhCIi/CRlzfYCiiYdxMkr4JJ3lxkAc1eVneQLFf/9i8JCP\nPn/QEX1dwiHjMK+l8A2zamYQE3g4O/Riqz1+mTnIDZI6UacYPH3UgFNy0ckj\nsptcFLHfcP4VuN9oi6BitUhS/rID2owtfYN993O5leMQvbpphLhL5dVNLclL\nGVRE\r\n=8Adc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDrrxlKfgBOp077rIsg/WEELexidu8pJEDTixJ69rLTmwIgdWmtV+x+VaEIKTbfNT9SVcNZIGeM6TZHlsxtwd2/TUM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh2j3gf1.0_1604441610454_0.4763068630461156"},"_hasShrinkwrap":false},"1.0.0-canary.kh2jnpxg.0":{"name":"plumier","version":"1.0.0-canary.kh2jnpxg.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh2jnpxg.0+4a91ba1","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4a91ba1f635fa9841f21b333722d6b035ee5991e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh2jnpxg.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-Eml+Q985wuZMgh3jYzde/CX3KrR9nXMdA47HVorh8RNTTHjjGDO7CusM/c/fmtM9K34Iw78u049ZtvriSwrlOQ==","shasum":"432a983c81a745e1b1acfafb4b06488fad67177a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh2jnpxg.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfodm5CRA9TVsSAnZWagAAo5kQAKP8MZZSvjwyYiXn4PeQ\n7VnXNTV6xpyQt7706ODhT7j/pa106VXfyRf/ehXxRjIma6R5OepPxNGH++Qe\nQNhNP2ElDW+zxK0iawt5zE2bLmT9HQrCaFCSf4JpJztypTGWhXHwNQkHXLbz\n9tgAvDWjRiXJPSbJZyDVtEEnf804ErwbyQu0s93iLraJv4XmvijUNCJ7ZwAv\nS0TeWT1yjtrxd7+sMppL3U9REtfkcwfArv01X4e+rnye9X6csuuO7R1AW72u\npEcxqvyAjp7jIwCSp6+e7Rs19ENzArnIVrh0LMqy2CC+QnfMIkXGxzlUwD6r\nJctglNLwMghL/xunGNvXmOEGXMNlHWvbIvstEfufOkl5yy+Oc68Is6QrON0N\n5YDm09mf9XA2VySlqsFJIUpEWg3s/7Tm6YedBbVHu8q00PVdmm6vk8BMaYfq\n4ah5HAkpXKGEzV1uU3RMJ0yT08ytcgVTuNlQ/qVtkYbc8DuXPpjxVRG5rZyu\nfFvnmA6D0hvjV78EjxQ5zEZd6ehp4Mfhxd0jzrVNWyQFS03Le99YqxbivE3f\nHwLM/Pl+lpOaDo9kIYFj1Kgapuze4uQBCIz/zUrXMv6eTStn+SKxM3y67w/e\n2GUTRMtuAIdXoIjBz3biQUS+9ZBJO7ouIWt//j5TQCEOc5QvBP3YenE23QwG\nxFf7\r\n=5YrR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB0uMn4PRggLygrAAqZN6gl09tVf0Zrj/gvVgKWFmxGdAiEAjY+yz/adk4S77JL5q4fJqh41NhJCs6XaeiHQoldhDfU="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh2jnpxg.0_1604442553335_0.24674852075860287"},"_hasShrinkwrap":false},"1.0.0-canary.kh2k6f0d.0":{"name":"plumier","version":"1.0.0-canary.kh2k6f0d.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh2k6f0d.0+e05c694","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e05c6947ff7d10880363c653710402a843e759ee","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh2k6f0d.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-2woulJWOm5EyEv8J5jF5KX6gXrvEA5XE3pJG0qqz+97IlSqZTzZl1xFFQ+A7cBYTUQwRVii3K58Okn6yVFkqMg==","shasum":"2ae85eda3a4f2b8cf75a91e1d185750aabaa55f5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh2k6f0d.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfod0gCRA9TVsSAnZWagAAe30QAJmg0jQkQk0gJo5kNxU6\ngk4tTPJ0VcErIo99YqJnwNGOWHe9FCwy1GXIWXHD1oW71xMYYzbkMzCxmBaq\nKbYU3ubT8FssD7qYBYlZ0ffbiDSZ0iFA/RbRtK6s9rL/OPh9QxGvL/w/T8hq\nsINSI73rSMliKZS9cyCPTdRgp5KtJKS2mVjXReAUzGHbXcQZAO08EYJsf36e\ngIJBk1vpH3Vtw+6fnAl8ouLQlqKMFBiacLEZchJaT5bQ0dgW0jgUIkS3ojzV\n0eSbRxZlZuqPL2uzZj/799SkJLNBENQ3Thg0Ehq3TtR8yqN2NTpKxnbt54Y3\nvNO9ij3s5Qa/aXcYjb9YIHVbHDlw7P8zF+fq8wkSXZ8xtDGmtVdO6TuCSzD6\nuEgkCimpEfauGowvbLXa2Sps/WIYSE6BvZlV7KrlbVp2Kabia0Mr5QHxRbS2\nPIGxJEXx3rLi82qwIqGZKKwOEqLwfpLw4mFgmzKfg1ksSuFaogZgYvCzsCje\nGqRYPYSYZyE3kYfVKeyABhmkZYWJHVZK59eLTy3gdnbs9fiD6ofbEvzAjN09\njZgYQFRkN/nEtH/7076goj1fJXOBsO8Kux17vPvCzCtvglE6W11oB9Wr3EVq\nseaRsgqgmKjXTuSEVMgQHRxgp3s0+3oQMlZ42b1V3a7ZjOlk3HehzM29Chux\nTCDh\r\n=FspW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHIRSpcOLx9UyB0AFwh/UJ755Pk5kKFf/wbpUKJjWhbnAiAclanib5Gh/d2gpfntzA4C2wznTgzM625+QzCo6tGmWg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh2k6f0d.0_1604443423872_0.7426205690028094"},"_hasShrinkwrap":false},"1.0.0-canary.kh2lwwtd.0":{"name":"plumier","version":"1.0.0-canary.kh2lwwtd.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh2lwwtd.0+1c0f97c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1c0f97c9b5b389e620642387a9801e3125207caf","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh2lwwtd.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-AuWhVhqHSz00HpWYKk27zkVHaBt/zTC4RntUtNyJoTwtKiQJRlUWqKWEWEKXpNnvxeP180gdPfHb/zwNuUTdaQ==","shasum":"54d84dadcf85251c71ce435fa874b9b5a9b65f58","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh2lwwtd.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfoeiHCRA9TVsSAnZWagAABRUP/2GpegV3eue8RvHo6kL3\nvhyW1qEYL26EIvTeuXddaLlE6xUA/K7KXAFW4RGJKH2ryar0SME8IsZ6pRDB\nvV6PaKh4SssnOcOERSkHXMcgNiguM44u/We8qgUUbs/3i213GrUPJkMgLjtB\nU+uK/sEvD6nxJH+q2vNw4MYn5CzCXEmh6CkDNWNxSD+yDvLPr2gkb8KYHYto\nGO3wDBiduNxNjuJUeSZSJ721NEUgKGgxVimeLpggQ8+2aXgnl0cms4CMMSPF\nhy4pnAhlXIpCEe4pPZWi2Y5F9We60H/D+ZIKQH5c0IAqQRez4FQgvvXCRZUV\n3KgYOsKyZPWqJLTnvMhP+jXggdS+gs1uW/M313oQTLeg7GLK7FqRvJBMaqPV\no0WsKwzOtozaOMwVLf8WYj3EQ0/NCaVtDTJd4Hvp+vAv4Q8p7PMlLIBJdCst\nxsfMyEzszQUpeHG0usTFPDxBl7hPQD872QNIabKo+SdxsGVpgy47tbcWeWYz\nsy6vIFWSKMi7mW3gOrPFJohQpcJQ41eeWuzvHLAkEFyFR4JmU63Uhi026pBD\nIg+tUpa7YhgGHNE0JfE8HglUV5zuWkwxUbzDFt82Gj7rcySrhXlfOefH+Est\nY1E+nqpkdLXS05SKapP/nqUTZ8ZCOfZSJz0UZ4xb74ILZq4f6bQpydjurIoW\nhMHO\r\n=Zu0f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEmYPRf4SLDCjIDJ6WjZA2AD89dQ5JD/lpiqfdQnxNg9AiEAkAsAZdCCsbDioZftPpVwh5OJZCIG96kmTHKB/sjxVso="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh2lwwtd.0_1604446343173_0.33846297662202063"},"_hasShrinkwrap":false},"1.0.0-canary.kh2p7j3o.0":{"name":"plumier","version":"1.0.0-canary.kh2p7j3o.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh2p7j3o.0+61f99c3","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"61f99c3da556755615955996608b0c00375f00d1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh2p7j3o.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-4amtGVBF++xj0rEHX5/OWspXLYcKd13vI2hXb2mfzJ/KKub9i79OOizgI1IJLteWSnimh+1LblykJQLhqo+MGA==","shasum":"ec50d2f906e39e9c528854ac560a23222b1b91ee","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh2p7j3o.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfof4jCRA9TVsSAnZWagAAFfgP/jC8TLoSOPwnS5dSmPsx\naFiRcSjYXwqzAvCcfoAOAg6MPTDQaerdatX+nmPMrNN8GA9UG/mXEAgKFccq\neW7cbr3LtZBFjm/TcvniKmx4W02++xepNfjMh/qm0bf4S8g5Rqf7ooMov12R\nsqvO/NM2G4dnaU3KHGfv+3CIRdvcXcsTktwZ0XF9G/IZovcssr3qozJaTzcu\nF3YIN9Gpx/m51cjlONBjyikn5hG4YrY6cdSkP+dWLFgD06yTYJUYNo1ZH3b5\nzXaDbdWKhLf4EhYOk1uDC77G3Y8AEsGbipxewTu9NyN4cfs5sxHaDO5MJG7M\nC+86S7hsHlIZmu0d99BN8YIz7d+YDSI+gfxOlBDYziw7c8vJfO3gVXe1J4KE\nETM5xDoxLvzBgZuJIvHdkmoMAsFU8GfgwMWSabd0FKQ/0W/JGBEn2bD86gO9\npzc/HSJ9iCZproEYjxQ9RmSuc1HtIzlyCJLXtse6wEJMZcLfYdaO/X/7Ktgm\n3O6dEys1AARxL9V12UnDB5u/7xOLiTui3+EhHX2ERc8gP0aoJHcGDkjc8J+p\nrdkxf/2C3mffdceQYv2TrVlx2g3rHEX5tH+16L6seSQ90cpaUXKrz6qxNeJU\nJ0cOvWHEZlEnwecH3ePQ7y2LFKxJkMNst4oltGGmVuMUQvZR9tCGRqepT21G\nV5lc\r\n=gavm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDMn6f06WtI5vmP1KGLp0eQ41K50+dYeviHHgZEevh2CQIgXj7ZoAaFhTTlkOgaHqmANUU72eg5+6UDJp4IydDjrlM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh2p7j3o.0_1604451875079_0.7762170725201718"},"_hasShrinkwrap":false},"1.0.0-canary.kh4ew4ru.0":{"name":"plumier","version":"1.0.0-canary.kh4ew4ru.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh4ew4ru.0+22400ba","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"22400ba78b7072b66ec3fee2eed9155ac4591d18","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh4ew4ru.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-ycwEa+xIJ6E2NeT8Y6C+/IsQYWR+4IN1WJIkMxV1PAxSx/nf5wQah5KQOFg7vVgeRaWZ7EbQnwnESRRGOP5X1A==","shasum":"c54b8c8d13b4e4481d0c673f9cd6beca033b9e5e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh4ew4ru.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfo5LeCRA9TVsSAnZWagAAHO4P/jT3PiP8AhYTX1VRdoVF\nNBRJbOOgbr5rBDuyPFVvL/oWNBpqjFS+0AdRSBFPioZmpOLeIcF+nzYw3XBn\nkekrJfZmt/RmcbS64s2ChcppsGGVUYO2qlaPLU/17L72iQMHOCsfPBBvBmDN\nA9X71ZgiCSTx/ZnP7Vn2ao6JHD3gUqDeb1wk0EMTq+Xwvylp37dfXUhtMgiI\nIWcufPzdaMl21yf5VzNQRmStK4JUQDoP3UlkVDR6znfhCwNkOOHg7vgXsHqA\nYc9q7op74MOkCN4D7f88XlmLCDL05DxMzWvbs39hRgLFkhJC9XEB8mTNUI80\nBvIaxS5MEMXglLoVUV7HKgOaTZERsna9yHIvbFzkKiimaBdvHQHXJvnr+SJJ\ndrFk6cpno0i4cKLgMCTT5tOiqF/zyTQ3ryeYLkDit6ypYKwlkOPwUFYzNfbs\neJtJimcBkcdD3HrrLaxD7dtbzaJGGEYcrgzNe+lvYKzzImam5r+sxrVoeq1D\nUumZ+nT4qbHCTLnF/a9GNvVCqyB1yMZFC7Q0khENzuxxTduW5JB17ZndPj7J\naaO0ndT3GqiQaab9ACCVS6mTZofHEwdW1kVnn/1SOsck7Re+rP/lXqqAooQW\nx2iBlD9cEfSWz4jQDKcaWxt+lJ+E9NceQsS9B+RaztA9rbm/AnP+LuNSr/OY\nLbcT\r\n=EQaL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBsT32qGlrc4lBMN4Hcg6wSEKf/1v15pZ1kU6N4eaPWWAiACif/IlHYvVjUcNscgW6TAPv2/pTowFSjdV/m8g1InAQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh4ew4ru.0_1604555486101_0.16829430551987534"},"_hasShrinkwrap":false},"1.0.0-canary.kh5ddq10.0":{"name":"plumier","version":"1.0.0-canary.kh5ddq10.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh5ddq10.0+495963a","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"495963a7de73e2c0551c46d527032ee0face0ea8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh5ddq10.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-BuAm/w9l0b33cgEVcNMIUpBSgU1GFYQS2Efn3J01js7FRxqQZeQkaqQQSemQb98GRbdaSlC+gqX5K7fFzilRgQ==","shasum":"1c8fd3546fe9a399a11e1af84b78ee57eff3e28b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh5ddq10.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfpHUrCRA9TVsSAnZWagAAC2YP/33Cd6yScCX800CrLYt4\nvAYWV6/QjY8yjJT2jTyqAxl8vC1eCNVmSYsXhNFMeqHV5yHyC2WLQx7jCCpG\nKMhpRcTmQ++KJvSz/a3Mdcxf/aLvrLw/N1cBZB1WfHQ6jMzqmaXYeXFXtoeh\nfJHFXRX2RlsFA6M+pk/P6Js+k/OpZFXAkBM54dm4CvVRV8l8ZqRIqAk5WXXP\nzMviVM0bpAbRLh8HIUsK3iS1rs4NnTFkd5Q2N+mBWNmgyDNxA/pN5uTIElET\nJdrpMJvb4u8BEtTS3v/e950I+RHmk9ul/SfA9Ti7D4RqweSwxIM4eX1Nuwu8\n1LZyUQ6u9TO+MDzDlP0F3OodxT/uRmzhRnRFMfLMtBGP59afjqgLx+TWteaW\nQhKT9rumSIE6B8VwQkp9XRuQ2WLTB78wyD4mHe7QJNjgU7KTphDQo1Q05wox\nIMFdESqJYh2hM2Uhqq8il/BxXHJTObjvQmf4ZqcJpjpMg9XuEypqvVDrNWnN\nFRGYpoRAg1MHA9sByhHU2FZof0ZgO4yUuO1Zj7QI6LP/ufFtfmm/Uo5w4Byc\ny/RpGufMSDGnfasLQt4mUulps8MidRjGqy7qpbvz/KnRMwLM8b+GLFWJTKCw\n086l2i88Kn2Lr+pKJ097gwaoWH5HzIilAmowvVEUbOyAyPGZBmPqfu4LbuIb\nzoey\r\n=qn3l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZDTDXynZGh4a125/2esLT9/GyGKsEmYLgnpdC6mslxQIhAPMZW47LGZRZ/bLusg66Nh6bjWB2gTr4fa9ibWCw5DLO"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh5ddq10.0_1604613419080_0.3855754118051009"},"_hasShrinkwrap":false},"1.0.0-canary.kh5h50n0.0":{"name":"plumier","version":"1.0.0-canary.kh5h50n0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh5h50n0.0+bd4dd0c","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bd4dd0c3cb7df9e61614bf7202dadd7cf7cb8116","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh5h50n0.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-4uEwjMUDTbHl+xnvkDaFNslRFZoDJK76d5hxTeJ+L60lUdBd2g69rjtGbZ13o+GU7iiZJB3KZQFy9IGUDh7mPg==","shasum":"dece0cd652e9ef4eab41760cca04e877240d920f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh5h50n0.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfpI3ICRA9TVsSAnZWagAAD1sP/R3FkZ/5TNZnudYxrYow\njnTugJCXNktF+JNfOVzNIOrWBwK5mfv4UnSnF6/7tC2poxZnO1dU5Q9yt902\njO5qzp48uDH65N6jrUkSDg0kIlkX8UHd1qtq3TxF+W+C6ijjmvLFpONObMEq\nv2bDM6gwkcaNLmb1JQ7+9/qoIbL39b4xfQqXZJm+DAiyIwg8e7nbRcU6/Tsa\nJrOSGEH+PYP8lC/bi7YkNzQWnCWuzWAkShbJ4UCKHCrcwcn/QZCOWzIJoydI\nyp6/rHHIlswxrcgpxhkcKEhjbkDeayFHQYsRxuLU3NlCjmSoQuW+vW4SfDCd\nHyRCoJ0RHzGm4ttUG7uoeQHYg4rXdqE/Prv3Ko4qOtw2RBzTKXzr/Hdilm+Z\ni0UmFsIKT6hKUHxgpdCWkGM75Ldhh7OPkiUFFRfaznTUozt/Mg+RV5S1UBUF\nyw8tah5M77A2NW6O4oGI+P0OZN/x2bzwTByUcXQDlkcpQQG+erJrRNVBbYea\nY2oymZIEw8ybYWKtqxtjjgZoFgzCFgVBu+bue0OV+79A+xIyuojyRY9Jxo4V\n0SC0MVpoAHFDjSPXZsiyF9QAzbte4GIwM5qNvmqPHcLkFdtN3+yk8Ai1/TEx\nkGRw1O51oLs1rTwZ0jZwxxCBC2eRCjkaURdFji9swotPb5xo6N1deP2UKaCv\nII7n\r\n=Kmx7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFb6ohf9DL0x5gS6DoYyTDqgpCh1FGYKMl4FedR/s0DQAiBA1YJFQLHQvC7UuCmq18y5JHOHVUfHrjaV2BAnEUrkIw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh5h50n0.0_1604619720251_0.8702560118040554"},"_hasShrinkwrap":false},"1.0.0-canary.kh5jla6k.0":{"name":"plumier","version":"1.0.0-canary.kh5jla6k.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh5jla6k.0+4e64306","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4e643063666c140bd0bc7e5b74d999cca6d507a3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh5jla6k.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-a3jtXuU7TOm5wKKy5VW+Ud/CKY1ijVohg9p3VaDitzsY9ua4D6H1hweWQQTLnY/o0p04c4xiRKzLT2WGwn4goA==","shasum":"71394673210b34b7dc9e12f1cc80ab9a740d5a57","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh5jla6k.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfpJ3fCRA9TVsSAnZWagAAcBcP/22HnvRL3c2zfUvSN+qD\nDtT5Vmx4nVbcjCNgbCdsBjQV6t0oZpLWLv0eOWxy2A7+BSAD7G5WUiUZxyhF\ne8dqGj9bGLDljH23hg53N69MEOWLDfzYNNFWPl/F4AJ1kUqKI+g91ciDh0Gn\nAm4nsqpWyiad5vmyXgLVa0ZOAAgSMLAfe7u92mIHGx9iz3KhlkEWejzgBhJJ\nW6Hu+25Y5pRJcZLSz3t+boo3jSGbwwwIwv98KbMS7Yqz6nm1ZXm22ALvamwT\nkI9Hq/OZU6kqREKfIDpigtu0F1vay0es/ToFqD9iRbEAf1DYRKxvn6BIaGXB\niowIVdd0EtPNrmSaRLY8tXKZmJGeXTt743hMK4igLFEe2Ykcmp5gwFd3dTTd\nO6JTBpjRMBAL4Am0AM6oM9/8WNx6tormuDl8eataVNmAjElkd40t3Z3B0cbl\nvrWlwh2jrfAGHrwoU0n9XCTvZBZ4xqYOlO7m9syA031yawbBImV9Nbe+UpA8\nu/YWAal+Ht80j3xmNpib2UdS6ZWAsvCFJib54r/yETeyN6QFT+9tkl9HtVHL\niQErFd6cZgUykYcfrOo7XNYa0WJG58uizTQNhwUyxjubkyG6nhS0RQ84KVmE\nwTSQsdaHKI6gC5koMhESCSFbYkGWzKDJskBf1dG8clLEkVbxN5vY7kP+/xGl\n4SNM\r\n=ZInk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICYiK4UjG6W5PAXB8CFQ1X07vib6OTbxmCpNyu/rHKexAiAbud6GKzQKjkhfmgwexZzWZxiNSzJi9sh5COUfXc3zow=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh5jla6k.0_1604623839012_0.19618551524485728"},"_hasShrinkwrap":false},"1.0.0-canary.kh5lyii8.0":{"name":"plumier","version":"1.0.0-canary.kh5lyii8.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh5lyii8.0+f1d8fef","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f1d8fefe024a7c7567f66ab76c8a14b242cfbb84","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh5lyii8.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-MPZtHuKogNXXcVmlQg0Ix2gpOb2e57CJFvpdLWbfcLmxs/1uXFoeRaO1ArMvSaiOGyFFY/eCSJ80Un9Jczg/sw==","shasum":"3539fb547882725f179f9fbc01f2dd7be34bbbc3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh5lyii8.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfpK1mCRA9TVsSAnZWagAAHuMP/ihIfK9pZnXWQpb1Yb7x\nal6pNTSzVw32+g8xArncpQZa1gWXqz4lyHd5ulJFZVNcwpIL3t15bT44RjlF\n5f/dmRajyAhswNs9McalUskG59i9OZd5Hp9caCv1Bgr5PN099h2jNCZO+wHp\n6VJSSA4hwUnPSLm3U/pur1iNe2oFooqir7gzIo7ZTZmKu3F4hWhUPvsSGfVC\nXJIl+63+bzS5jVMWyCX+Zit6WuAGFe/VwCRRta+yPhjA9NwhxGA/69kAFmLT\nGQaOWiHVEm3gwk2wnaCr0td9N6461AILDqdb8ko5cpNCk16/51F51n/gVnm8\nPkYnzdyojKjBBqv3FNyjmjqcxeVwJDWa5fTyH2ZzOyn5tE9IIAcOMNF8R8W1\nVj6MpBthajhe3NBRtMe+XXOz3x+sm2rQSNw1GaEeyg3NQyZuqSfj27QTT9nN\neMzU79IzLCtLU/KmokbPvw8bAfu0IpWybrSajxGp8vjpAroHG/Lb4izp0NHr\n2+tj3ie7+Aa3ovp28X0QdCgK+od0EES3LqXdFOl9ceC2d/bLU06ZL7T3bko3\nIY8R6Xn6OzD7wPDX40D9bNyVFo9leYbXzizftB1XndTY901uABYbRVOasjv3\nAlrv3S0ONpErg7+YeL/tEHYbYMsdV3UM5u2OQOv2WmPvgsFnNMRTkfRC0F3k\nBgW4\r\n=KjDo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH5B1BxlhOnLiWfDs+8kCusjdN4+WP+SN60qUyHpfWeVAiEA1qgojB6FqoStYr1+kkVP8GRiu7heCxe0tDnGY4SSKIU="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh5lyii8.0_1604627814246_0.6009594462480052"},"_hasShrinkwrap":false},"1.0.0-canary.kh61az31.0":{"name":"plumier","version":"1.0.0-canary.kh61az31.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kh61az31.0+ffc0ac5","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ffc0ac5f47893cbeb596e62ed5b4fb37184431f9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kh61az31.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-b9rrhTJWZSgPE8wKC+wUo29Vay0fg1Y5ZOudBclDABedox5Lvd0gM+9zE0rj0QSP+O55+v1JpnQN53UEvWdlUw==","shasum":"0da62e7f8757239640fac59d10c67e0b65a6f628","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kh61az31.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfpRIfCRA9TVsSAnZWagAAqzIP/j2MeB4JagJJ663z3ZOr\nmKh1sOhMMWfoJsuyARp9LJDjrLbS4eXTGHX2SdXwl8nm/2gz5sfQeDwzsXIt\nJk1k2nPYl6CxVZdGgi7gzU3j25tMUDCLrY77PZmbx6Aj5b+H7+M4JgRsp5o5\nFGV3WzgjPu4B70uplIobx8C5r90uMdPjlol4oDvMRes51hg1gEy1se7lyUlw\nNFryNvrqHeuKo6PtKCD45g1m0DLTYG76XI8YXHy2xXhvWxYn8iRaq26mgTF3\naa8za45fTbRg+LIeyyj4lJ26WulrC9XY8T6Iud0GFe3BMvNjkhWmt/NpnVvF\neexeBdo8hnKuD9yPR8SUMKequqiiEJmP5o/qA7fpY97ht/P/Hq6uj6RAKl/F\nmKZ43itfA8T1Gsj2bncvTzd8IQ63Skxdq8RNgEveowajT2mxm/8WtZdtCtBV\nNlm8We8vcepQQxN1u66dSDz8ZTQI3kTjMqEkHwjIlatPCGPSSgUgWmtvLXEX\nennYpWAOIoRlejJnbEzWeK++HC1Ji4/t2yWY0ILx7mvpDZDwlX7KTgbLdBCq\nWBsWi/zK2HB2cBIaSUyArZd1GndrflRJQF0nGih5G9FfXvWUzZwV0za++48v\nxf/ixj34YGSqldI0XRukAJWts2B+ubme0a1LgCgp+eZoWA9/cQArJfrTiRRb\nulLr\r\n=wCzw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDOUOCd6USARsW+y7Tv06E8QYh4Sqgifohjgy7VkuN26AIgRwrF2WcG2RXpeb7ucw8oV0SwDMdeYVquu1fspSRC4pY="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kh61az31.0_1604653599395_0.7876552324031605"},"_hasShrinkwrap":false},"1.0.0-canary.khbdcr5h.0":{"name":"plumier","version":"1.0.0-canary.khbdcr5h.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.khbdcr5h.0+8cf40ac","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8cf40acf9659f9d721b395d57b1d6cb7e3d22343","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.khbdcr5h.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-A81Dv89NohOYQV8UjzHrxId2l9D4hntXk0TrqNrI0+cTZ9KW+4Yn5wXwFiWj/w+mMQNQx+NR97BmI6Rh45IwKw==","shasum":"d3183f34824f9129ae7516bc84186f57ba7118d9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.khbdcr5h.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfqf4kCRA9TVsSAnZWagAAAqgP/0vaZyKQ18H/YxRJHi9V\ndrrp5O7GWJTwJ3F+63QLsIcZWeMtYJpZ+PT15Q3w7SShhMYInWjIJAYnKR60\n6ZxOFA5mTES5txl+mo5HlI1WVdb9+4cqTK5ADZ9YGBHCeHreFp7PKf7zoGKq\neoWJ4xAKEWBjOUvTJA74PSeGG51vWbFPwSPEffQRV0DClgO9M3gMiPTqGsEi\nI7BpAf5c3nGKHzGP8wDPHyT1arTZeoXDGJ7X3W64EYUocbQMuKv7FykxThGy\nyByGzHuz/2+v398qfHyLiZNmfKB/nPSqgs+8SjzqhpIFd09Yrw757qnY0S3/\ngN/Ys6lifX6VXwx10aVZm4e/Sf8T07qUTnqFc93Z3fizh5YtNiBT0CNoM51+\nEoVmqbswt4YZpiePTduHMtqBJNTTvbCx1kxcc8CHLpRO6UIuB4pK2DdTAPtR\nLpSv87orxrO91oMQdxuMDH/ElyFVoxD4w5po8AzG56ALrG7Fz9SLDtlF/STr\ng/WDacwV17ne+UgjKHQUNR85Jzk8LxiVMD95QzKeS8ZWUlelUb1I8REenSIO\nQ7VVJpLr9m7yxszLxawlc1bFugnz/kA1WIROKVLd5KfpVpg2wqPTOQGgQ9cf\na2h2axUSIxCXMcmpGA3qLx8dm51zhAi3EUz3W1OKyek74YlkS+7JraGx0aDx\n/aE0\r\n=cCmM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHRd7J58bdDl7zcdT99gvH626uz4bjljEMtBd6omCiyyAiB3UutMClJNaq2lkHCoAG5G0NHjIJbLmQ+1zglRIyvF4A=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.khbdcr5h.0_1604976164405_0.8641905088403001"},"_hasShrinkwrap":false},"1.0.0-canary.khchhyyz.0":{"name":"plumier","version":"1.0.0-canary.khchhyyz.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.khchhyyz.0+2c92bea","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2c92beabb1d026fab857e78ea3c88ef1e15d832a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.khchhyyz.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-ZmgYTp0IVEVpYixARjp9mRKnubpaHoLb8G/65MiL04E/g7pPxllQhDHeA9BVyiKaPcBFh9rgDaXebFdlq4+66w==","shasum":"7efa5ea47d087ef4b983cfa332491f7a4cfc1ca3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.khchhyyz.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfqwWECRA9TVsSAnZWagAAbpAP/RGEII0ShAorXiZOPhmu\nunI/CE3T/ASuigsLyUjSWgYPrI0xKw0eOr6aOYq8SEA2JUM7mmA5jZagxvsS\nh/jCprigK/SIPKjIclrsvD3oz4qQFp2PTOendk6Y4frb/E4z/kE0SLNnfUEZ\nIQO6g96SYA9UwywJ7A90pbiLAogtP8Gb+YNvNO75UjYgsZzou/vspzDW0p08\n3awenK7zBKLsDILUUcN1lftnCLxkUyagYlES0oxZUbUNLLe9SpwQADoUriB4\no1QC2EH3NXULViTS7q61yzRIeFgUpTwSyDplKkNGg8l/P033M9I0fwDNPLT6\n+llaJV22soztRU0B3udSWrqweB/VYFBn+nX9VqcGJFtqJDnmkU1qFsX/AxRV\nayD7ySseQzxWmGKvAR8KX5vzFXNFBKi+dlm7ZV9AtD6ueGWb4Z3GlPlKHDjZ\nLLN2KAYT84Gw1OgmRsz/oiP593qxoYZCAX0r/SWiLGJcYwcSagtlxWNbIhmx\ntGSW0sxE2ZuyxataL8sd7gEm2otKZLqDxyz825m3Fz15unpZ4aRP9V2Ekc10\nxGUquWToRwRBlhT/1jUwgf/HZAnjCy/cF3ypgPIBOwUcdNo52AIqKEJJEJI+\nY8XbOO+VYbsnvlrJFxI9lBtiQahaJOrUUvr9UNsinDYOKgTYMY+J5H+NZulD\ni83x\r\n=eRUK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIElTVhAKpRVGbKN2LiR1hYZ0ylYhE6IX6WTYYca7oSNmAiACFGBgCW1/kFzcTEZDnyS3iIcQqtVHZsR5qUozb5mKtQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.khchhyyz.0_1605043588321_0.08100303955941324"},"_hasShrinkwrap":false},"1.0.0-canary.khdxsl4k.0":{"name":"plumier","version":"1.0.0-canary.khdxsl4k.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.khdxsl4k.0+aa86213","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"aa862136af5fb25886e857feaad7bcc05087ac12","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.khdxsl4k.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-B2RwVafcs0qFEN9ULd0sHipg8MkyKuQbJIAkdQ4kIcI8L+MmawIfYjAlqTJEHKE272+DDelGH6jdk7tfEsUubw==","shasum":"2d845425e183467ff7c55a96bf6ad32c0d5ebe1a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.khdxsl4k.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrFyfCRA9TVsSAnZWagAA6Y8P/0S7NDPIwW4gyXjIZc8n\ncjVZB7jZEb/uaQ5ETTm43pB45qaFxMou/Cd3uQHseqosp1citt6N4jkV9Ay5\nuWIedKSwJ5qeQh8nzb+2rR4jmq6LEOf++ELuQf4+xaBlaXuVsdOjgiQSjIo7\nwSSA1UpEgvPkWEEi4eV5Zgtze+6lvOKi9SLCaHCLkccu6qfXF5H2ku5fdlqq\nNzOxfSnUT0fLZD/DNDrTwivI5etc3KEN4gMcB79PX8qzXVLbN/l5IPlYUMIu\nbBfrxBliOlkpFnh0/u31EXMYh3iJ0IvUbIDxc0rGSMcypPUN6eJCTBCgar1C\nlySCTK2Ptts0LpfWkHP6M6oglm3+j+zSdIUIcLmgAMfVVSyNhBpd9tVsIcjL\nY0fPoIdDpDOz5LbPTecFejo4dBLDpEWMQx8++li3igOPYDZIDutvJxtnH3JR\ny7JFWiwdBzr2v7iRJEsGY1oDS/7MVnyb8C2MZCbR75y/Ut2T/uq4XUGSqswa\nLyVsISnxMGbCFhMuygPTQOEXvFx9lSXNYS1Y8dx5hhPZYGMx+QknFW1UkscK\nnrWi06B+/FSNNzKltXvUm6uUvVgmjRt3n0uLZ0I6mvr0kEW83Y1QMe2jJrCW\neBjI7f6zNUNWHXyGe5zUYtEtmzgedpuaCoGTbDxgKrudg/SfpZ4VrhQt65yV\nHHjR\r\n=etrr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDACJAMagCZWNUDN2l5heclhMFwVevShIy/5nkEikhsJwIhANvOFmjiB5c4tPFObjwf2l61WHMFNHBlP7sbmfw9us2j"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.khdxsl4k.0_1605131423074_0.8646609292089034"},"_hasShrinkwrap":false},"1.0.0-canary.khelfoev.0":{"name":"plumier","version":"1.0.0-canary.khelfoev.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.khelfoev.0+8bbc781","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8bbc78180abc7658b87037719c1e2ca5104da79d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.khelfoev.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-4nlxqa2zYbeqAvR1AaSXKEcwwHzq+R9oTwjR92smmcgmPjtMs3kjtCp1/PteibLAD69rjZZbcRr+9BisX772Yg==","shasum":"c573c7d1be777c2f34b02aae78940202ae80348f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.khelfoev.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrPe7CRA9TVsSAnZWagAAqRoP/Akzp5/PfYAaWpwd2WM6\n3jprDq3BzA3FOLmuv3ywJYxO4H82AKCfrcNApseLAAwV6zsoH+JL7I/B+99Z\ntVZtBIjA6JQI/5H1k9vQq9AQULGCAuL5z7aDQmmjRczp0xGoda5dKwCs27PT\nUII8NmHhRfSEDkV9opkNoCBxUIAx/Rpnx2oFQE3ZzDRu5y+xw9HslOMIJDgl\nQhQN9A3+BlUZrQGABTU00N85srNA/uCiu6xVs+T6L87m4SoGupw0jaYPqqcD\nUKOI+2U6HjI7f5VRfk9LDJgo8qJUO3b+pNfBOHuZ3zO6v8ztGvyY58epkm4u\nUM7Z+vj8fD/ALdgi0pD4dOxmbUMQG0SOgmvsmgs/2TUItwKZ2SRp9llqgTEE\n8ePShs0luSeIb/uwrYIm7FJyf+MV0IGo4lecGll42eaPI5y657RItzW5tp/K\nBqYWUE+H9nJ7mkLYfN9L/+RjNnq5zo6PEBweZA7iyfPiTNmJ4rZAzIv791j3\nhCMmOi7+P7EsDGj9LWi//soo2zfD293xiTIQ8uwQvN974nuQFZ+5FDB0lA8y\nQv5kQGqBYiBpeexnVsWnqEcdmcLNLZ4cycqtD01LKW61NnuYqjHXOXk4QJiF\nE0N+b/lLk5dO+BgOKYHKCxAZfWJqN0DPa5GXLYLm9F7TEFsQlXD67ZtejaQY\nooEE\r\n=2Auy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAul/mwrDBvzN8E8w26ldeEVqODqr0hXPZTXlJP9Y4XWAiEAsJe8upmeua+5bXYmAKQn9dxHdKB6/5kzr2uQbD2rTQk="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.khelfoev.0_1605171130981_0.2563074503317575"},"_hasShrinkwrap":false},"1.0.0-canary.khg09zf1.0":{"name":"plumier","version":"1.0.0-canary.khg09zf1.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.khg09zf1.0+321b263","@types/bytes":"^3.1.0","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"321b26357baec80d3e2ea7e85b2704d7632ea366","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.khg09zf1.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-jB1oMSPJQ3HEVRo6TGKyI3GNK26vGtlUlc3DlfOYTYwyMNHlg7ZosC2+/ynoif3j5GjtqH83MG/f/hr992hSgA==","shasum":"e114bb9193327a7bfc70e58cf9ec7961653a18b6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.khg09zf1.0.tgz","fileCount":13,"unpackedSize":34103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrkVTCRA9TVsSAnZWagAA+LcP/3k3kbAOVu0Dd8v1EXd1\nj1xyCcCCR92HqTjSauH/XAYo9KbEIfdmp5hZ7SLNi6z9CczKthOwU8KU2pJ4\nOhC7J5zw18nzlF6JNxRgdRE6DE+OJzqkXvozkZPdXaxe88+W6KAQIdsQzuIW\nlktTAwtExKpsZe4x3q+IbuxQ8/OVVDseDcNVfXVQgA1RGiVwlZLPX1EGKNYE\n52c2+zBfnliQR2W7aBytC6nxRKU2+yOAgfbC9A61QsD4M/j4fkIYA2eyOaPQ\nUjZqcbIEVqatSPkN/NDCmm3qDlQnDBlLN4IjNrRsagDn6WVTGmtle6/h0Vrm\n4eQI2QhBeVrZQn5wuNBPaY94PlT9Rp7QN28WXHlbNBqRqX5fxa0BBWly9h2s\ntIeXSWR8ImWCmzf8DZl/SffMjVKsZ3RIugJkpP2DlDfDfxF9L5ZGW9BKvQj5\n9vXmvG6/GIDmWACrDz5P0NF0BOu5oHO84STG/sUGdtfQi0QKrPS+PUJBW/xc\nMTsp6WkG02xcdFjXtdawuL1UiPiLJGcXRbxoF7ohPFr6/A34UkAKWG4GHpNa\nguLqZaafdo9LoKq0MlTAr/gOfzj3qLoZiwuIrKtTqGOx3Thqdw8bqitda279\n0dXv3oBuXPU4fGp6N8cCGNcSChqOBgZpg5mX+ZD+RbfQs8pnTURePVZ+kjy2\nP0ke\r\n=j106\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxr+F3LKeR6OTbnx8V6v/njdYYsrf8EYNc/icHpZ3akwIgONSbQw7LcH/DKXkMhOG+NDUnTt9j8r/ChoRuZp9PXB4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.khg09zf1.0_1605256530570_0.9100860044144541"},"_hasShrinkwrap":false},"1.0.0-canary.khiras28.0":{"name":"plumier","version":"1.0.0-canary.khiras28.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.khiras28.0+68b4cae","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"68b4caeed2d95cd808fc0c876a2f71a12ce69e20","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.khiras28.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-enOWfk8qn1wuuCFNNQ3yDFaNP5k8Na+qg0CbaUdsIccsdlTRBXwnGGJQViWNqFO3tKsGGfHch4vbSi7oEVy8qw==","shasum":"77193f0d7ab713d489189540909d396c463240d0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.khiras28.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfsM79CRA9TVsSAnZWagAALIAP+gPx0Kp19SaXeN3m6lTv\nEOqxkA5cCxWR2gVe5n/FyWiAxT5iNK6R2+ldiJv5CT/ayUMGfwNhk1J+hAQt\n6XflAdyM9vP2nwPCez8s1E6VVHd/c6D4Sou+DVjsruvolOR5XkLcsY75WqcF\nWYQ8Re1PGQfKnJQd43OJTTwIjbSeC5oq0xiP1yK794zMk5pEqi31E8oSRWGx\ndwvUvzbew3Vlif/CCGL0sFrfA/GZ3R+UkSZ7qE0r2yCU1FwNy9BU72Eugh+g\nioicoqz4XSh3+XmmfMOIjTGFl4ze8d91gL8VCNALvYllZPJg0hyM6PJGuY4O\nirgEGL6XcRPY5bDDWKTFu1krEwMcbDtxA5cEf6FhQg28AGaW9lnwsm4Sf7jk\nZsHrCbOrpggS+2oPNYzEXkx2fx51ablu0LW+nNvFLq+VK1KulvAYg/56TF2p\nEFbLR++lktgucB3dZ1Xvr5SI85sw3YHUW8fs3VgSDmH3SKfsQQsvM8faqhMt\nTn79WJ0YUIX3hpWJKjFVPss5ZsGd1oltiXTwCBF/OTWnM2RUCW9+ha0tep44\nTSqrcap66d0PMCB6eDES7tkmMYcsvv7Ya2kJNsXNXitTtMaLcVaRumz/t+g5\nV6aJMqyfim5eraTvCchO6+qWEjKDEPaiojtC+T63FYkMgnmpE9T7buYcT5MV\nMwbd\r\n=0ong\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCCA3slIeHwOu84NhggqqIQ013oTOk3FSs+O798xkVznQIhAPgHvkCIUvmHoKmDdPvnXnUZGkhZe9D8/xNg+Pu7/Ly1"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.khiras28.0_1605422845120_0.7480068278353991"},"_hasShrinkwrap":false},"1.0.0-canary.khy4xbmr.0":{"name":"plumier","version":"1.0.0-canary.khy4xbmr.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.khy4xbmr.0+5941bf4","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5941bf4349c3ebef81a51f961e4a3aaccfcda32d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.khy4xbmr.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-oSUCCFZFrr7eS6pocPAEydUKcnDvUULl8IB/OsTB1FJ+LH4twbkAW+LWGuYZL4Cny2A9Ad1JTiehPoaKLoSL/A==","shasum":"8bdb8fc6a9d4d6687c60d92bbb819705d13c1e0a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.khy4xbmr.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfvv9WCRA9TVsSAnZWagAAxSUP/20PUGnhn0QU3dVn4DBS\nr6wTkLOIX7B4dcS6d5T8RIAF3n647USCDlAjQoYWzBB9xDqBOY1cQc0/5poO\n5aBlFvVHFHxEBD9H2an8vf2Vwg1lQvvkoO3HBgQJLue0LdtWVWKRn/f72ZbR\nZC40OeZ1qxh60x/xYc7pXh0VW55JLN9ZNCWyDpDgybkLYV4ztj5N4q/mSj1W\nwjXsW92idgCU7TAZL5jajH+Tg0NNLrRYnOXFm5VqFaQBHNm+RboUNMybnQZq\n2I8piogZeyHafjSy97yGDxWuKE2xJNxhSoFYMyLSb00wSC/OnJdc/WBqZgNj\nNHJpivo5cGSVUJxP9m/U3aUq2jaW9oY73r1TU3zTOXO3BXe0MF5/PZqESC2a\n9OHqWp9wyen8PU0gBqwrCaArc4X6MV3yssM2p66nDqN+Bxb98SL4fnqJh2CD\n8wnfdxp7vxxUQpGAXG24OKXHIcxlyfzGgTu2vqv5DfdGa5XJe9NcZdsvNLGU\n/vNYS1s+8aj+cIIBMLesc3CV4fFxR1Hr2foba7RT/FH56D/2UlGE0CYYfCB+\n/GrnmbaWqe6lu+deJYl1IAxdwzo+YmIVWhuxm75TaSWqR/eEb7j7YtWK277Z\nJvkJ7ejGWoN+B5vXxWrzJm57zGK36GAfLlFCtkDlRxsimKXVBpQfVI1Nso+Z\nf73w\r\n=vtpW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID2svAGEmuWm8NdArjaX+aZNnieGjzylN/MnsI8ctwKgAiEA4zKtRUvApsMtLDAIymRddCvBKzW47UYIf7pH4Nt4aHg="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.khy4xbmr.0_1606352726254_0.6818328552461026"},"_hasShrinkwrap":false},"1.0.0-canary.khye3mjx.0":{"name":"plumier","version":"1.0.0-canary.khye3mjx.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.khye3mjx.0+028cda1","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"028cda16da32c4d838a9bfeae9f1599014432b0c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.khye3mjx.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-1GolafjpAxhlv9NglOJ6JXsgRUgOKk2nCuxjbqhNpMH8ywCwpNgJoODgAAF+rnUIdU4Uj02VhN6kqI1duIhmjA==","shasum":"0bed3ce4a0ea3fd19d1b849a21f6202f8f7e1964","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.khye3mjx.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfvzuJCRA9TVsSAnZWagAAZowQAIvyaVlNmE0YK/5zJx1z\nJBfYQSIHk7pt7JtAFxzlv6O6uz7O0H6ZwCcZ6ZPiSVrhgSNUb2O6iHYHERV0\n0VuEtLWvVlamxW1VbQJxVBevaZ6553cJ79bCTo+0Hk0jN3NGnzNv3JI9Y9UN\nAxvOMQgqZl8kAOp01sF7FeQxWKdbnjPkvxqcCF/uBS/8PjZGdMNDhKKRqNH/\nKV7x+M248didu3pQD4mzbTmhuai+pd2fTizYZlqZqaGFRlSmiiVofi93s9IM\n4YAsxmS+zmos1bLAQ76klOQlDdu0aEAWKzDoMpAyqmGlGhWEMH8P0IPDBc0x\n+YHw9YNzjrJUPoCOfLtIXzuf3lRuz0Z+KBeWW2ARj6cSivl/Xdb3PAJMWRST\nyh4JAhF22k2CtDRVS1iPsPUui66O7+rDrpvdOx7+z5lxlzNIfQubjIzI21a/\nuiexbhiaPv+0NLaF5KcBpD5gmClhbzFK3KYHfS9QIYCwasoImS+teh47mRSn\nRiP3udLN6SlZ1ZQQUAVLLWbAym/BLw3zKNgVnNo/aJ4QiSzXFNPwag7eEvN+\nJaI1sQJqL1vpBUZMVVKm3EOTpEILcVXkkuCaVHhYVKFheKBbIWUXuMb6Ek7S\nJg3GaY7jj+fh2SaUQCYCpFYCrbLE+mJ7F1Nfi8rh9VgeGwC6JO1VKdnIGPJl\nk+3O\r\n=Fubg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8hOapq9MJ+F+6DiKAYVB7Q+2ni1NKCyor1KFFh7a68QIhALYx3FfP6F6lIrjDurrYkzPxEK2c96LNamVnNmsApt2K"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.khye3mjx.0_1606368136765_0.3287694633490068"},"_hasShrinkwrap":false},"1.0.0-canary.ki2idjwy.0":{"name":"plumier","version":"1.0.0-canary.ki2idjwy.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ki2idjwy.0+56bb41a","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"56bb41ad262bc68693ea9186c2715e356582d886","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ki2idjwy.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-t7u8gdeFTAzy295bAe3eTNgKv92irrz95sL6TMq4oDVdL076MbfUVRziqcXilnyU0rY+QgPROLlbsPvVMjqyyA==","shasum":"bfe432e5c6b38b76cea48bb9122828e0bdb50985","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ki2idjwy.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfwwhdCRA9TVsSAnZWagAAn7IQAI0kTSnpcwkKzgpU9Lh7\nP8TFFP/3zF2LWXjNr6FeOU4Kuyyy6rt70uO/DqDz/7Rq32YbyA+HobeXyQR5\nFf+HDuAGciftM9muXbNDnBBkzjtFiXgR9zxXsTsjvpv5lru6gbz56/OTye7F\nqWxRJExVi0wBFjHGdVdXmDUPwHr6XcMgy8PxSEPGZGyAWEWz2bM4mkjDEhmu\nCKkna6AXxau9iqCzGyo6aPN9HGclA9vdNNriyNyww34k5y87kCu5L+O59YcT\nK2CR0D6yQno4DrKLRjIDx7hap0Z01f7I8vNEiQQ/h+FQKU352EAt+l0DDnSc\nKEfX121SHHuucl/AS8xbkB86Ux0aLmCMj2ZqxtUAokEYpsmNnOJX6NLeYa83\nkTY0RM34M8wKbqNBpaJ1VlyLtYYagLjYad726xT+sUXqYjqnRRoj7iTjHvF4\nknBE5HZjYIrRrYPe92YjYb9NrkYa3tltXuKWllrAeXU3d6wpOmE93hcH+49R\npBsz1QzhXzSShZStMiafFUOtt1Gr2oXEXLPtvHYtJxWJ+qdFgMTbvl1usIzw\nVW366Ns7/AbBhzaeRMJyw3jXYbx9GKZFxTbHPbfB1EUtr6iY6QCmqS6IGziU\nVcr+kO1ByOb42aWJJ9XWb6ccgCl+24Z1nckGjY4NPaGcaKp6Fo0rWLzdXKpw\nZH2j\r\n=6Wl1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDSAxu4Pi2QaxEtlIa4OTEhnJHsEoSmfe5VogHgY19hiwIhAPMAUAwDLdBMmqdQfNZt/knQzazqUYiarXRmW7ka8XKC"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ki2idjwy.0_1606617180936_0.4314653545331484"},"_hasShrinkwrap":false},"1.0.0-canary.ki34emgi.778":{"name":"plumier","version":"1.0.0-canary.ki34emgi.778","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ki34emgi.778+7c0080b","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7c0080bc977b16cf2b733533f15dce836736acc7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ki34emgi.778","_nodeVersion":"10.19.0","_npmVersion":"lerna/3.22.1/node@v10.19.0+x64 (darwin)","dist":{"integrity":"sha512-Lt08998MdielzLwself4+GVfktqmih8J+rnnFGd/wDXV7nXE2+LvnDHL4jyxvZzVsmTwUIkiBs3OLma3FO8jTg==","shasum":"c50ab2bc4928c33514d4721f9507eaa5006d0fd6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ki34emgi.778.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfw5jnCRA9TVsSAnZWagAANIcP/1Wo+Ygl8dSvqteQ4BBt\nlO2Hyu74ynf3yk02HteCkgbMpXSQCPvct/l73whieOF7WrkBXf40UpgYthxl\nlBsq2bHA18zQNRZYjHw9UNPZyIo/f4Uu3xRoW//CeUcuCtNZVckclNaV8ypw\ng+Q68r61GDYTcrKJKoiWGLieAjr2VNpVhoHQeGlC95jxCxDo/t0lrWWO3NG+\nWvm7tpvGMKTNVCGcpyCMR2Hc4EJqscQtRZPW5VsLUoX0Erf0JBj0FPD2GW5i\nJK//+VJl7L/BUMTeiCmqKxgcyQPMshrV+pfeH1dGrpbzBmFY64FMWGxWqQoK\nHurDBbtzhTatdTEf+zz7QdiNBao4MMRICp5r0gXDht6sS3BbyLm8ebcad+xs\nmRdEyv8Z+x9K7wwkTNsQWHLsrvAZ0G4F15jKw0T4CX6hvlbE8hFg/TrbqiIK\nU/hweMAbQvaKFQvLnb9MLdlzfN5/oqFkLTrYz40PKJ9/vg9XMBGJ7HKXuxnW\nUKQk9ID+yunsDQlnwqHglTxK099xIL8xg4B5Fyh++Nr/6qv8f45oIaabduAa\nletaHi9SquiDqFpW5J+fMyAyNJZV+R7KC9+I0wdP63OvRTHQrh/cY1X2cXGo\n1qv0WSZLnbM2maaCUsAKX8pGvANLHiUN+XNVZu0YebMZVrr16pFSDzKW/vuA\nFBo1\r\n=LtE6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICEfq+7Gx/tEkvJULiaJs5rxzp1rFwcRNdy5n0GL78L2AiEAm/WwDTXC/5NeynDfv+8ul/DcW1XOCKlzAVvUPHMNYj0="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ki34emgi.778_1606654182827_0.8430725257280067"},"_hasShrinkwrap":false},"1.0.0-canary.ki3i90va.0":{"name":"plumier","version":"1.0.0-canary.ki3i90va.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ki3i90va.0+0a18e03","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0a18e03d481ff54e227ab4b2774c42ad34164f08","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ki3i90va.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-CsWmzwWZE7CB3qSFl93+BP4lOeghwWSPs7+QX4fZosYnk8JX3CC/hfQ0kcp1tW9bSbUFBi6s5USVibiFeQdssg==","shasum":"cf978a35238a69bada86abaa86c682266269b61e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ki3i90va.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfw/O8CRA9TVsSAnZWagAAw2EQAIAZpdPwARjaQqsoqgTo\n7ab6sFt0h6cLZXL1+Zcv+wEPfmciw8Vvu6ugD9cosLohr9YLkFBy0WLm/SYI\nUXE0W40A9zeb9JLFuwLzeePPLjyhoNks8lJnjzYvW61P++MMR/N2RtNSOMx8\nuBI/C1WJ6VJ+56o0JExZObPqjM1vXamnuAzEW/KgHBlv46rCHpsLnkWMfPKR\nA5qRWScHZyLTyCZyW6iFeHYYRwn1gLlligmPVbvLBt4SZP/ocqfWLnyZFeML\n5lHRfdmNYAlXRUWime60/GU0vj4xtKL1+Q4O6lVL1I1omG1g07/xvYygJbx+\nkBkJ526aRWG9LX2zRUrzMSaz6VRIiKe4LUb+f03f6vS8GavB3j8L/jtRHqhk\nE0zMDHqGAiBKFEhd2JgbgpuG4BNV1WigUJhMKUw+68lsw7FDxFGUAc+qDpSr\nD0m5ezZ1L1zHqvf7CWy452iJA6jKWm1fn5oi2Fh13q9dfwHsx6HPGROx0I3G\n7zq0Y+I9xJFOTxImZwsDBLj4CvJ6lBSWnxLrrsKmmJtffkRmq1SByzP7Nv1H\n0z5SaJ28ealBjF0YrkdLm+q2oU2SeAC5T28JYQFLFpziNZ+MqX8E84g59aqb\nql4bJToZ69UyE6O11oOfrnJrtV6C/XABr100xTSRIYaopVLm/5tIW+c4j6bj\nFsR4\r\n=n6Qv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHlDw75lyVY1fCWgxrY1g9rtO24yilmI65W6/yvGl16HAiEA8NA5KCwYEk2LWKSKGrRCI3cUh2UAeRfCLVyHHHZ7Iiw="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ki3i90va.0_1606677436064_0.11586097777207072"},"_hasShrinkwrap":false},"1.0.0-canary.ki3imu3t.0":{"name":"plumier","version":"1.0.0-canary.ki3imu3t.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ki3imu3t.0+fe84b4f","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fe84b4f9ebbfe82900620c108f1de20e952749c3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ki3imu3t.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-vePozvRg6CkvBfbU8dpXoYvHEDuY9rmuArj1oO6w/e5YZN6MghfNd1vZAkbocjhAly3EyKk16pWm2gxccVs2Qg==","shasum":"c8f41d3d0fd25202a024e6e65355c224fdfec11b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ki3imu3t.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfw/ZBCRA9TVsSAnZWagAA1ysP/iF6GGxOR88eIvLG/oXL\nMWHpOkGB/p7D65RrDN4M4ho/Gzx5w2XJB94i4FReAq6+lJ+8VC0mdeS1E8Xc\nAoZ/rrYjbmXVbsglpnm/RmYXt0kdP2Xef9seytDfYO+V40l7OicYSwheMxrO\n+Ji4AI2SMu7oNPVpQztIeWEaiFjw5ytbFGLrXtes64mT2lmV/2Pe+8IEMXPl\np9ybh4oQaRDrVdcJVXdMbVuWI6UBw+sD0qM93W6/NFXav5k02tQUrl8owiTe\ndnlZEDshyrO14CcZkAXnolfTRlAIhvIQ3cUP6JE9+9X9aBcoeb2ervk2p1xD\nxA78+wiZmRpuLoThIxFSzTnqkyVyge3rWYeG/69ZAybchfa3rj8uvdnOTbp+\ni9/CQniPHuEHcSP2YBHB7MKnStgw90jfiAgdLPN07cJLS86yT+yRHLOBBR5m\n0GvsvD7AZvXH1G9Iwhabgb0M56HO7WX6z6qE3G1D84CUodeX6Xg5dmXMuQnB\nXF8N6HDrUFtL51Ec62U5xvZcjXvnTlzTNhuVL+Q/lDPh2atMkSIv6C2kAwX1\nDWyeiFNqPuAF4sC4OGnBzV0Rkboe4rM4jZPomuZ8MYNGUZY0NOfHYktIMm/6\nY067XNmdZBVS6wT2zy71rjE+Vz1ezp1XdqF+i6IBVJ0Gdss7n3rxvC+gxzbR\nus5B\r\n=nNtD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDlPaa42VDzq8WUnN1DSwWtKBLYg/7m1bIInk252tl/jAIhAITMRzXFNkIxTLR4SsvJT+RAjfpd2pL04ICtVDz+Wtl1"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ki3imu3t.0_1606678080855_0.47638403114569106"},"_hasShrinkwrap":false},"1.0.0-canary.ki3w8w7z.0":{"name":"plumier","version":"1.0.0-canary.ki3w8w7z.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ki3w8w7z.0+d0c04c6","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d0c04c6d33c2ac4eadcd55de4ed5a4d0a4b143bd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ki3w8w7z.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-75EVzQ7z5W5Zut/WyJILGypmvUHEMT5kBw859Y8B5+mUf9y1cCmpArHHVz8d2LGNyYqFvZYMrSm3hTdueJuVAA==","shasum":"d509bfb5df078d7f511b7a1c6d2f8a15f055633d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ki3w8w7z.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfxE+RCRA9TVsSAnZWagAA1oEP/RBgVXdSZU0c8EJFgMLc\nWPNqJyGaQogRkinJF3yyvJNxYO8qY1/jp+NIeQAJnCG3bi+Is9001ILbyakm\n+18M5ZwxC8MgHwyia1WSSJU2YfLNvyiFB4uRZn5vlR9zBCKezBzX6/YDr+kF\nelPm9IlyBGfaC2rfhMMQew9STc6agptO+FpLGD+GWJm8hoK25kO84OrX31gl\nlZasFDO14b2QNl4slYbsvWE6vvJC8H9IGnjwg0/opiyI8049zIeRTQsSUb5l\ndM9YkFlFr1smCmVdX+4bxYQR5gVPH3XUQG4pfMH7VMBDv5mad0MLwKp7PGf7\nxlTxHZyb4V3shI/sXw41RYXmDG2df2BDCwgTsXMbc+x80cyoRiPcSb4CtPjP\nssdrfDS/UQec7SdxL18+3yRD90L26UWqs8uZqDgwOZ66BvQ4LjlXOW+B/mC8\n38/9Ri6I34sBS4rSJb8ttGoohBZHF5BGTBeG6GAJvtSqyOXIj+hHfg2Yj8qR\nGQybtn1zHTrYFA0WwV0SJCKzcw4OWpr2OFbUIlhH3RKfQ/1wQW+yoeeUUIab\nJ06vbKUgKBDEV9yxSRt3aR9TFlFznzA1wKGGUPGYP50NX+WWXPl9sFXCpXFi\nU3iwdZwLQhYSPgXlooOwDwRhv6Bw6WpaJbCnCq3E3Zxki8OW3MzJzgN+0H2z\nlQ8C\r\n=Jero\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEb7VVbS2D9HTNEAd4ODoLVo3Waj383NrYvvtWvu0htFAiEAjO5gOioQaiC0XN9Gf6KTOZwHQg1Tqt8dyMH3uVbP8Mc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ki3w8w7z.0_1606700944812_0.021495432991815777"},"_hasShrinkwrap":false},"1.0.0-canary.ki3x3sog.0":{"name":"plumier","version":"1.0.0-canary.ki3x3sog.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ki3x3sog.0+e371310","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e37131074c87abf70db6584fde37777b83588104","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ki3x3sog.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-59xU5vMuUQvyfe7DXIAW5izfZbtEjZZWj/7D9ZO+SJbZUeBUF6E0jYtDW+zkXAQy3m4Jltr/O00JS0kWSxEaAQ==","shasum":"0d4eaa65f14f9b1f006cf57c3fbb89499c8afede","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ki3x3sog.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfxFUzCRA9TVsSAnZWagAAcDIP+QG3uV5ZwssYo0NtPe3e\n19r1kSTPujNpLZlMvpP6SCDQGGKFmNLo9H3CqYohqMIiK2gr3QYmDHJQyTWE\numyDOkXocQjtMmn1Ca7hqQ8hBAVZTOwwqPw3XOR7F7qjz4+G/0yA03soqRQq\n1tt8pUlFt4yvyRXrBvH3P4jqpSA2EElUh9UA8Fw4tumlKg60UxEPAtP3WUEb\nea/YNTnX7R6MwYXNkXZf5RYSpdAgD3UkJ8PZy8S5kPbIG/+38CYofN9kyVJR\nVWz9Sdxe0I3WSH2gMpnz2QAGlo4ZVMJ2C9fDQE9/01jO7RVJaNhGP08a3t8L\nSCzuEocOOYggA2uBU82gLw//1lGHetCQjEwtwDQocBb9YD/sx+s4IEMvfTGX\n7z1BC4pc8wiCmJmttOhhstqRxhfMrAeBRWvqGe9tA7YR31O2HHCwCL6Sc71l\n5lVvHjaTOeYBI6GwMWTYCrrrhMP1RNuoaNhmlMrEeGNaql6NUwmUXILBN9W/\ncEijP1Bt5i3OM56uvPqUn/HioqGqp910pTfKlhxbJiHhem6gxJlmuG8TUbVL\nj9VU6BCnrgtWEaqlA982NFD//fwxFSFAo+/KabCZ94MYzcsV3dEKIozmA6qv\n28CWdJpMdE9QNAcBwAVbaPsGu4WZ2YpvHcRfcgz5kqQ357TaoE89ZRbQAzze\nKM2c\r\n=BK92\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCL/s4UpvYlLvSmzEipBTJ4dc0fLypi/WkacGvXhERxBQIhAPGCzTiJvrFuwBAAVSPyZXEjL8zM916zv+L4V2uayR8U"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ki3x3sog.0_1606702387470_0.7737817861589513"},"_hasShrinkwrap":false},"1.0.0-canary.ki6vxtqr.0":{"name":"plumier","version":"1.0.0-canary.ki6vxtqr.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ki6vxtqr.0+a931fb7","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a931fb773dba6f069b38df347f355a29eba54a71","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ki6vxtqr.0","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-sI8ad/eRlhXnJ2vbGyhh+7pB6gMGAm1QwAw1qNYBYbNF/Tze4ouzDvSEOGhflRzkgZ9nEPcW3FR50IJxJYYAJg==","shasum":"e50a5864662b75bf5ec6bb2e5b0e1121d9050370","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ki6vxtqr.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfxxIkCRA9TVsSAnZWagAAYpAP/0++tjvqdZXQJ2jiI+6M\n0W0/tu8OMmPbCRSyj8Z74/klpIt6Wz7k5HXrdM80Nccz4zpxtiUFXrFqPBB6\n1eQI/UvrcX0JamdxJzzA7w2/xn4aLP1LU23B5nBHadCPK32it8OrTbQcmfXy\noFWyaXiWZG+wJAo2F+w15JoG2gjRxrXj+ONjDkGm0DCtcAAyBwl+8JnhTVER\n7YLLxJ3zhv/sF8gR3FIHNeH3T126jNi6P6E9sii7a+UaAaNUeFnxvDHLZ15t\nphgcZ6qx7UMwAyyoi46stynKIKDgv+2TkvDfWFuijXfNn0mV413h4NdKQ/bB\nDIwyf7NQq/wNn9FTCCbDkllgsIfxDWUZPYujtLckjTmAVm4BnxlS95Bwg7hU\neArQyAIFG7D4jI2/SNmz3fVgvvZGSiZxufjC83Qbt34HIlZ94eWvNNuyluBe\nYomZcCHRKkKbKL1dP9J84rAJrWY9ypdcqMHciLu9PeaEX9NylcX2j6O3u7FT\nAr0ay1ugSmgFGGS+Dt5d51thxYooGdeiwTA11raf8lE+jDIZRC/VVkSCGdsF\nRnq24ZzhvZGUalAz/7oNXS68sE/G/nkpv6YS9+kHBeQmyR2Fx5VFV+S88B0k\nJGz7GhtnPSlzB4jypik4/CHDwi+LzZsmSZDpHKuwhJufFPetNEDB9nVbGK4C\nGFfC\r\n=fv9A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCddLK79H++lNOqtTHLQKzUweaucwEalKWQe5DoaimDPQIgNHx63MAqCBOABLXuHYVZL4oAR03gX1+8XYh/gQB6h7w="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ki6vxtqr.0_1606881828462_0.19173721465623328"},"_hasShrinkwrap":false},"1.0.0-canary.ki7l9jjj.0":{"name":"plumier","version":"1.0.0-canary.ki7l9jjj.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ki7l9jjj.0+fb59053","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fb59053dfa51105c0c9a307a487f87045eee7518","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ki7l9jjj.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-Emq5rxTZViw0UVqVgGQYOj0R8xc5wyxVL/SgADfYxQE2IL7TfsFQIpd0/4vxKhwa9FLO85vSXxWCXEOnC5WlKw==","shasum":"36953dbeade816aad071fcf11b69a6000a51856f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ki7l9jjj.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfx7hVCRA9TVsSAnZWagAA/NkP/348i2t2B2Uz9/+1f3Ze\ndLfy6R+VdDuiZXM/i5LqVqjfvnq9oafSV9lmhGDviPhpydk75Y1X0Q/tATf6\nyJQcglGDVgjvAPaJw0sRTOGqX0ZL953uv3w/Jb8Vy1E/olxrABjSa3xGNHY1\nDZi0+EYJ4Y+F9eRVkl6SuZNKOawQS2SSU5+flfXOmFpDOPuhqGhTE5N7MR54\nZoFJoFwBjPgy8vlpNaJTCg46wM7y+Yv8luwPUa9xVuuzYbdBtAlT94mU4Mfc\nwOlxBECahnDakUgNZai/EuGxDko6Krqp7xmFLeMIZkY4+R6gMuPPnKaxQFP2\nnl7qCMoj+HKNQXkhCg44YzP9OTe6VKqH35ZuFUEw0qO1piQM6DZP/pFCkQVo\nsLxcmnWiVM7BgJV0zkAlzH2uldYklNq9GxFidcMz2KOrg7raDJ/mxxn7P7Mt\nV8knjMFRJF9vhRtEBBU+15oH7RnxJVG928LOENlnR5bYE8ADeHui+LvH5IkY\n3eYvt2CUof3wZOMvmRXu1VukxXDHye0Snzw20yzCVic2GscGjOOs19wxzPfI\naJyzyYq6d0lurl6dS8iGjuA0rKIzoqd2H/wKQ+m4+RzwbMGYk4WHIbZW5/ea\neXo+1+nnFDBY5rJm74JlJRfs0iiOQi6EsQRLwl1UlEkG5UNBtBT/uYi5+dXp\npga5\r\n=dJsc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAPVGxpggGJa6ssng1AlJEJA0gRM2nM/xQrZB9AjcifLAiAr+0vcP26QTFaRe6T4MgHzj5CHLu+b9g6aTMZRBbeLqw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ki7l9jjj.0_1606924373034_0.04112191577698221"},"_hasShrinkwrap":false},"1.0.0-canary.kibdskyc.0":{"name":"plumier","version":"1.0.0-canary.kibdskyc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kibdskyc.0+629b163","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"629b163f15360fbd0bb448cc9732d4c146437756","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kibdskyc.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-F2bD7vX2mhaE+nYdvHw+2AZ39a6jBdBDyXBcY4WkyN9g5hHdcTF7k/6xzDX7PpdTU1rcnuZksc3CrqAkmvQGmA==","shasum":"e16055ca4c95d79116b5c2748b4cc2c432202a76","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kibdskyc.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfyzgQCRA9TVsSAnZWagAA8soP/2nHbwaG/xYCYKfAsvOK\nhci5a3zDk6wdBitjRLQ+qwvPa15z0YBQilxTZGpEXQKlR8YJJAzErHiH8QZX\ntccYncfdrbgDs1bbYMxRYNXvAfWcjgUzNUVhvsSiRf0DsZ3ff08O2yBJqRpl\nfueNTXzC4ffPK3yuVxOoQsfWghaGIo0zK2YB4q6xtH+WB5Mjk/JaJUTP2psQ\nOH9m2N8VVtPgN81HyqdL1Ohvzhlc3Urk7O12vDCEpoU1pNdFIsQelpR5K5Sz\nGcAfBSVwYRuFsWUko6NpqfBzevVtPzWRJPB2KjqErmqMxdED/NbWknU9UxDw\n3ObRQKp33zF4H09f8lUU0pMq17zceW2C59mUEC05hydiErie/4/J7RDneox4\nZPXPEOYjDQ7Jg5k77dVuekQAA7J66bNvegdq1U8s/l39bsztFEuLFOPO9qS+\n9HDlmgGB0IexJq2FZRlLDQGHz+yQHjWbUDJqtIGR34r+bcKNchoZS0ILYvX3\nhoVBC49B5FzWM20k5mRnM0g3trWbzxgAn3VxjakqVa//IPSeszNS82MCwSzc\nM8CzHrIVt+EYoIP+jDdsKQ7Ojp50QKTe2A6XFMPsIEwRKCMChdqhaRlzuRNb\n9OzY/i66eFxM5CCk8XVQsN3INYGvtw3LcklwxmxBgO6ZXgEtmqNmAhdAhSWX\nS/NT\r\n=fRTG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDWPwkyyjV2X4bLiS2URuOsnl+I9JKq5USJt7oKA/n+rAIgCE++wmmn1M0aMlFOhCqXE5U7zPGNKnzoUcgn6IdrLIo="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kibdskyc.0_1607153680447_0.0035032298806674245"},"_hasShrinkwrap":false},"1.0.0-canary.kicmb1c7.0":{"name":"plumier","version":"1.0.0-canary.kicmb1c7.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kicmb1c7.0+8d194e2","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8d194e27a3a92a5d05a355ebbf0c85c945ce7624","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kicmb1c7.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-2d86n8QwE300fNntpJ/m3BSCRGRpbcfxo/ZCHaCvTyn2i0GtKyLzcKELR2ScpMpjsV/SmFtwJlOgVqUS5RuzpQ==","shasum":"238aa2b9d3f7a56e2b0845e3b2ee4c829b012f2a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kicmb1c7.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfzFwdCRA9TVsSAnZWagAAansP/A5ja5sJc+nIx4WMIxVp\nyaKSwU++G6oMiECj2zMW+nATT7vvvMonIegcd+TstWFknnI24cunE3dZ3EkO\nntVq5jvP7itWLnlyFuUGF9Uu1PK30GucCdj/gywciheuVPRltJ0FOendosmL\numYP8AAPdMDfXM/SiLFr7GC3UL/ig+RMw9FiCkMZYPkiAg//Eoi5/axFygbt\nPb+/WU+qv0S4Wg2NkS0QLLnx4x3YOSlyTeRyyflj/buSCAeHF5JdyEXcNtmW\nd7b/y2Y0tLAY7df1erUUM2P3P6mBRbsY33K1LnsbLhTI4s/L5UW6lJna79Qw\nG1iFnsth4K+9VwxLAYWgRQXmeBLs9FMt7xhS7Ht3o1Y76YdVuUVhh2wqF05W\naafbdWmxpTimvpAB6Eve3L+NSK3egBAlbXrVedhmF/w4sO9gqMFCpV+KCwTE\nRDpp6XinTWSkeb4PMT8mIprkaMyyK/fYBNSkLrGceZdIKHrDVnvYIc3A8g9w\nephkOvCRFgEb2ARQ6Op8J+sVuvDih8TCLbBoBOCZ2EH54oxUay9E7eqzvWT/\nx7diS32c4TLcmj61CaiWdCBw5zobguI5KBGnyD7dZQhf8zxwG+h1bkkj509N\nAhzfaMJ4pqQfleP9dJgW5xM6CuLj4C1NiZYiDbQf2A6lV2TDm+//w0Wp4kmo\nK1c1\r\n=CfJY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDzcSnf8E1c+eWwl0v6GWeourN2/g67nPDBelBBTI4B5AiEAv2MuVIugJhkZ9+l45UchV5J0l3VAL1Rsl3DvZspm3qA="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kicmb1c7.0_1607228445088_0.022470528629585207"},"_hasShrinkwrap":false},"1.0.0-canary.kijl4du6.0":{"name":"plumier","version":"1.0.0-canary.kijl4du6.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kijl4du6.0+9f4dbcd","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9f4dbcda252eecad193e47ee70220dd28311f1be","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kijl4du6.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-xB36GNPcPjZSfSi9B5ML5kjOk3Tr+4d+7lYCDTUfmdq22ecEzoKL/9WMtF6NJYSVKzedET6EXUlEt06LvNKcCQ==","shasum":"bbd0e0f55213f06c6c192a72ee72673c11d488ca","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kijl4du6.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf0sm6CRA9TVsSAnZWagAAMkUQAKRKY+AhDsrjow2Yhaor\ntS66nyNy8JRSfuOO09LDrI28IaJnGWENsjU/gccHbb7X2TxGFXO5Qu5acMOb\n/U3Pxj/pWjuw/rv6n2E8+bkTYL+jtgTXGmxHyh3W/Z2CuaUByXhgP7jtLhrm\nOgjW70G9NZu6lhjdu3hKqHaYSUdhvGyCfF5bKE9egv3IdeAfcw135mnivnME\nwIJB8w66bz1aq/DLqjoXdzeC5c8xFJDPH4Zms4VxTjiovK+6JdRHJFn5Srp6\nO3kvx6X3sNCYxh8dNmkRAbCtJr3kSFQpmsux4fxjZee2IE9T9fqmbVpvieX+\ngABs8hG8+6vKZ+Jq4CDOaWIVoweOVzjxvz2LhL+SS4sPd+MxrSIJtanILCJO\nZqEcoqHUwAIYD9SZBA1wiUD5mmqw4lo7KOyixE9lrJ0Hd5wddHZQrsxG+dLQ\nW+G0bypqRB6UhXf9qKswJQcK9X4WSDjQsCCMA52T5RLZ+obIRtK5a+ZMrYEC\nSaWXBdbzOxn92JGwNHhy7icSIYL6bnF3CnKjzVc3fiVUJVRlN/6RC4WEbpIg\n1V1bWuViEhCs0sB+bjHznO344DYzo4Mnh9rYiBkvmTRjZfzklw8L9xO1woZZ\n8jrqCs5tX4xLF+HJtsthQGjrl3VYX1nVtymZ61nCRx6gL4RcHRxmuUHvQ2Z5\n1332\r\n=Ju8u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEKDpomRcgGN4fxzsGGRIEjaVK2+/SX+z9xsyvukkPQuAiB22QD1s99wfNzexi3KUoCtntCNFSqvqI3R33YUQp5YFQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kijl4du6.0_1607649721666_0.13810759405374462"},"_hasShrinkwrap":false},"1.0.0-canary.kijvk681.0":{"name":"plumier","version":"1.0.0-canary.kijvk681.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kijvk681.0+93c4776","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"93c4776c63a7577c9e1c430d25a1a90d4189395b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kijvk681.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-WSybMAKE/EKi1jkef0ZbfzitlVRTRf2MQpN7qHJftKgPMLWS9D+h60b48gWilgNp7aA3OFJsoLImL4ikNP/HvA==","shasum":"5d227845486730eb63fee394ec7f45b78ac701f7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kijvk681.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf0w47CRA9TVsSAnZWagAAFlkQAKRgcxXD0S3IIGfHrqRn\n12r88l8W7lmcSllg7afXhZVcI+ZOm/LVQf0Ttx5vZvKTsYBDIaZv9MoUHooW\nAm5rzn1SumzEoxhLUJvadh7KPlMgoHKAYVXsdMZ78EF7rANVqHEwFsrmFpCW\n2gDaV14cgaKC8uewS333x97g+SY8pRsnZsAIICXf3OgEBsACETb3Ih5xpKV7\n+8+0pB41lTkvpZDmC1BHRhNozYBD76TeLC/oMN6wX+80jBvZpE3c2i2lIuCp\nccLKBRCHA/uwizh1Cgo9nBpmoUp1Gefp+idueBkDS28ddvUFXLD34FQHWbZA\nw+8eplThCt7jnIO7DE45WuK42x4ivK/n6Gd8JGhalxgJsmfh7ElTc4v5evcR\n4bSISiRKOIETnE9WG1B1rOOvvpfejDxzEvtN4Uvm55dAGOi+KNrzq65+YpAZ\nvMCukViJFJx2CbJyqbRqR0aQRMOyRerB8+5oLQy6sqkKlPO2mfJzCF5B1EDZ\niTt/YaPDNAMCukdNd3ieFkd/L+5yQgtsTPyvJheTl0YGPpbpfAFTTAOA6M4x\nTOEkTMPm2ofySNuO9skxpqAwoPzjnDC3ADkCMIdZWhoYwG+aHEtPGJjuZVqW\ntmWXkQSNg5/cuM5/IP/0HjjulR76g4FSEfnKifjVjtORGUxecXyE4RMj2oHP\nlY8w\r\n=V0VS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFlDZWUI7hGI4PbbTyxmQZZkKCkTqQQotgDItVrSucKlAiAjr8rK60GAqNKDKfwYz/ZzGA/bTYC4GQUtKiq2nk96Lg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kijvk681.0_1607667259174_0.15283390107605488"},"_hasShrinkwrap":false},"1.0.0-canary.kik3wda7.0":{"name":"plumier","version":"1.0.0-canary.kik3wda7.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kik3wda7.0+4c6ac59","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4c6ac59976b1e9bf75998f39ccaeec896d300322","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kik3wda7.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-5czMbIkKQ8xc+0d+1jLDGGCa6IashSkJPj/taxQqyubCTwKglcNB9uTmoY4Wf1E0FVBZq++GJ0kAQTIxOKapuA==","shasum":"df32ee1e13fe56a24614faf6809b4a43a48a5a74","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kik3wda7.0.tgz","fileCount":13,"unpackedSize":34132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf00TyCRA9TVsSAnZWagAAju0P/ic5P1UwJZB82tStw+1f\n9n+iNlG2wUICvwgpEtb+Url42OPM4Lv6b1Xl2GFrBgxmkahLLaJAp09XThjk\ndSNka/LfSbh7/QTXhuOGUjJzExDE6i+S8ZNaEw99vIOsD+x+H2E+epOQee3P\nFZAVVlhUH5Hk6G7DhTF7xTYuwxF6TDJuHyOhWEPKPtlWGHQsdTFRBnW5vASi\nwKK/I+Wpw4QWvRv5q85Mm+xqcomvt1h4SCozbQx9njsCy9NW7P484Xs/qwQo\nnTEVIglZJ6WOi9IiIylLlzO2bnW0Uderez9K4NSb1PEODCHsxp1/+kPicxoj\nHolGK4ME2zbr3eT+rVCrKNQGElqALu9YX+jlErY+NdY0/DA0f40V3bWnIQZ4\n+yuR8B0qxJwXmqux3SPG7WwQkbybUSZhMP30AKGGCj/RGkZVpXZeP1FhRIVp\nY4KNKijdVqotcOoI24liFQQ1Hri1GYdGzn0vifM0/Si76XvtqVVXvX5LGYN0\nV+wd4kzmjOXIdVzI+eCoTFh2JwJ+HB/0Rt0AVFKDke4dv22BFhZYFeu/jS0a\naqLA1ZzdMC+XRrhRLLLvk9Bai6wuwl4hjk3U5mFGDzlQ8zFBb8bLdtRCeBC7\nfYdp5ExmG59WbwNTmuLGC5SXA1FhhWechAg0bD6jyunAqfWsBQLs6NWCwx7q\nuKna\r\n=SUkQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCVZoYCMs+E5GBwzi0Jti2Sr4vwtqMEe6mpy0+wftg4OwIhAKnIwu0ZbWtRVBEkYKIVb9Cpnrhlsr6wCRIMu86OkRkH"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kik3wda7.0_1607681266131_0.6128794666240367"},"_hasShrinkwrap":false},"1.0.0-canary.kikr9t32.0":{"name":"plumier","version":"1.0.0-canary.kikr9t32.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kikr9t32.0+80a2fd4","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"80a2fd4813c844999ec0109b833072a4081058f8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kikr9t32.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-ZynjN8GkGVoP81ZNRgJhncW096G2u0n4JEwfFheVkK55tw/x3CIwyHXiBKpNcFUKDL6ihAYhgX/iLo8mAizWqw==","shasum":"4bc2592749f11a3f75d9385fab13429be6dfa98d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kikr9t32.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf095SCRA9TVsSAnZWagAAUVwP/3REEJXE+LO+CS6IIxTS\nAnvKxvtjTb28SHetgBbkCIgumwxZR1nzC5lJB9RNxIECI5CYSCNgwMxMpIgY\nu+lrf4RXfdKKPDfJEcChqLAOC4jgfcOMWIesLSlgMjofut49X0Fbg9KzVGj9\nNegABnHXjCpWD38rXnZ92qAovWGfeIoMUac4geNWb6XdwBv8H9qbb+H92jUv\ntaUP3me0uzkv09mVqMF+po/uYgj4m/fgvWsKOZcpDOOW9g92jk3Y/5E765xx\nMXpxnU0IzqOwQV5rjw0Y00V+jURxNPnPjifqS7JjeZ5ozo2pJILcdasLEHJS\nToHzbs9Fi4KGiwHEuo60LigbOuSuZ3CPPaYv4Iq2i3Tgw8UfYfgZ1+4BBzsE\ngCYbD4l6Soj35v7xbs0j7AENnTjDAHd5/1fGd5wie1StA3AScOx+3rrpzSjy\ndlXtim6zJyOGeevjRYW3Mrku8DacDpkPfJKyXyh/Puaqk5n0TGvapl14tvUS\n2pSBQ9yeZKjzgeRffU+wbh8n8DhBIMwMvZm4fyBUGTEcPdT4xd3GgtN2sbFq\nn29mesAc3PFI/dZ7iTo/xmpqbFdVGxYzsbC+dRlZujOwNSEHsojPGv02tbyG\n83Pj6KbfgX9k7vrgZf2b4zaadmKCuVEEDNPRolItrXFyQwt/uUcAJRHSfLjX\nz/oL\r\n=D725\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDHtqYNv8uClD1ExrfaPUtmpthtAviFAwdrTGSENcFGRwIhANWBBYW+UX8H91LYeF0CjfHs5X8La+aEj2R2eW+lPxMF"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kikr9t32.0_1607720529634_0.08476142302085132"},"_hasShrinkwrap":false},"1.0.0-canary.kikrxvdo.0":{"name":"plumier","version":"1.0.0-canary.kikrxvdo.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kikrxvdo.0+3ffe579","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3ffe57982faab93130a83c40fbac996c77010e2d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kikrxvdo.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-RTERpfv7F1K0KTzEHppi025TNoxsQcDH4VjV4hf48RF5ShOzjhLQ6WUv/D0o8e6JIc7etUCxGWjRkw1PkkaS9A==","shasum":"e72e0b986defa218a894be69e20e8bc5b657c2a3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kikrxvdo.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf0+KlCRA9TVsSAnZWagAAIfAP/3ynxjtLXSLbz81DV24W\nITiLP5L1HijqIMX1C1ngaVAn9bNPQbCCgFkFTpxdffmCQvwY7872ow+a2ZY1\nJyc3CmoJd2LNtYKqmnTnR6VfWu0IQo+YGXCd7z1SQH6yMd6bKCFR50dX3tHW\nyqJz6zGyiHNKRA6zw9fIUnuhFPblmU+MJ3zx41rp8+X2bWzpMhfq7GQBzFK/\ncOZPHFjz+ZKQFRX1Ia7AP7c4Rs9QvtNqoxd+14CzeHg7Z8vuXYJ0Q9Rawlp/\n2hudJM3FhwqfiLcRMRyG58McIhTz50jNpIlLcw3qhr+kUBcPB1bS5/gg8Zc9\nrnu9zM36doLdec7ICYqymeEUZgAQ+X6vU3fBtAO86rGsGUYr6RT9NAnusQhb\n85E3quG0ewWxpt1/yx165xPPWvyi3r5+oI1A/A2uw1P/KTryX+Rf0F/Uhur5\n+6zmU3/BvHSnfllFYM7ZEucItgdkmFURCV6cpTS5tbG0LI/LyvJFcYWMoH8K\n+odsVSQ4StKIqnsJ77t9sQ3S52Ysf1yOeZ1nd7fT4Wu4HaB+3AWm01J49tYT\nPrVxwwNYcyQjyrOfIl1ukhUKF1MOFssePWsZAqMazHBgmhhE2gRRDscYAgOC\nvt2ihDgS10ibsOg8tisMxg8Zz8UnFUMS7lhccxw+JRuKVbcFmYJ/CQJUKE0g\nktlr\r\n=akfg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC27yigwBdqzHzsfraSFBm2p4OqcvzNSP0iqktl92qlCAIhAOMHof+We7OsVj7s7eSiqp5UtJdWDGKCXCTFA8yl9UvW"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kikrxvdo.0_1607721637132_0.9942267355708565"},"_hasShrinkwrap":false},"1.0.0-rc.3":{"name":"plumier","version":"1.0.0-rc.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.3","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"774d4ebdf67d5fecc195746349cf6b22d137e1a6","_id":"plumier@1.0.0-rc.3","_nodeVersion":"10.19.0","_npmVersion":"lerna/3.22.1/node@v10.19.0+x64 (darwin)","dist":{"integrity":"sha512-0mbWVWOdaalNlN2UTJTJjDn+hFa+RKi8uwgHmiLD2hO4gwGPhteSCorIqRbUwm+9aAc4rfQg02pX39vBRw74HQ==","shasum":"a18d599e4ce82c707887010dc7c64dd15504a376","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.3.tgz","fileCount":13,"unpackedSize":34094,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf0/TrCRA9TVsSAnZWagAAAHoP/Rt67rtWGn2a9qQpAA8g\neIU/9TydIxT+S6Mh7wqyysMf6pGwTNRHhqedqTGrQVakTXARx3eRmcRLfgHE\nXbI0YMos3E/bCfAJJClUuk80yHzE89+RzB55uAG4sOeQHmnkKKawc8GA46d0\no38Wx5F3uJH5aw5sQx3+8y/U+nNjs3Z/YkCIW5mtDSPDYrCWZObdXpyjYjym\n8isQBUnVt6HboRgPdRSaDV4h7HcVuWvc6t/g0yGhMxXBQFtwrbI6umQGHOtF\nwT9EKnw9B36BWEZxiCrfwH3Rc5X+QAPMtSMUG9TKREiKMLsywiM9wYwjo/G/\nDhfWjDNJncb+3pIxRSzgDaPTY4BE4kz/rL77xkLiFcjMF/tXRYVQwoIx6CzY\nBZuBXedsTcx2ZYz/bEq7htym5RqZu+w/w1U4FVydFosXe/wufvNGIfrlWKsL\n1qdzlnpI5HoaXRYwxus8Jyjp7HiM+dp6Fti3uoqkDUxY27hPXIYUig7nwrOw\nXfR+Vwfq6e8TwXM0vN38+lMQaG4StZT1+BhdyZ2+u0hqPj2ZJJBuCnIvf/y7\nLT7HMG2emL7Gp+ATnId9Q8Xk3g5BFqFmf6g4C1PHKcF2/8y3JXWhdzY05n63\nNzHRIVNljQA5ZMUoOMYok0k2W78IyPVG3oX6SL9zQcD1kZudHoPp4rjTtgBy\nMj+y\r\n=J5Gv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGiL+2G8TtOvFi4SqNOIummc6+8lVVOLOmhe37kU8OhbAiEAs5jgBvcNh8SCAImQP3EGvjEghfSdVLIB6WWdiiT1Cr8="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.3_1607726315282_0.5390701927242614"},"_hasShrinkwrap":false},"1.0.0-canary.kikuveya.0":{"name":"plumier","version":"1.0.0-canary.kikuveya.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kikuveya.0+774d4eb","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"774d4ebdf67d5fecc195746349cf6b22d137e1a6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kikuveya.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-xSh2NY/zbWl84KVtzoV7mdS7FQOiR/2jKVPTBcKsuCev7JyGULNq/dckH+/HgKAJGOEwi66z2t27SqKVc4Z6sw==","shasum":"598eee558172fcbcfc5706ab27a2179449f5be3a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kikuveya.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf0/XhCRA9TVsSAnZWagAABtQQAI+caFxuQsfUsYeL2QK5\nsCEJN9HVYkJwNB2l5qVS6bPEEjiHzOxOiE2ySOw1M7FuehqJME//c++/teIA\nc8I2drRuoU7Q41044b8hGUVRHm/z7L/4eAjTrKV4ohAci9mn2umuaVYE7I/9\nPUY9W3BsDG6UDZs2J+0PDWtXKwzhXO8lrt5HPO9iQ8dMShsu5+oi26G9r7ff\nyLRn7LPdOvAtobcTogehMOKXe03mZ9pYVncKv304ey7wK53YVFAuFSfryq9Y\nykoaABYe2/ftn3CwUu9n75igx+NxTJHt1Kl3T5cqgG4Ri+SJTiFk2TechP0w\nxdfhBl6DtWRQkNXmuGivd+6BXnLCk71pHNuH/ZQUv8H+X343g6584YeNp2Mj\nqX/fFNbuMnCR6F5DkoMOUY6nNGP9RLrb7Qe720ijQI1NkU0aHZfZtA1g8a0s\nKTJklCxAWgPc6ZuBcu2Am6LwzbYYkylhdsuMYWSTBg7Tqum6+pRzhK2Hh+gq\n79zGkctxrm54anPP9u/NTchsUQ8ovdlCV8kycvbuTv7VKfKZ0t8NzGIc7VZz\nTP7ZSjjZtEJUQkwk273GhKhJQ84fCrTE67WTu1c3CpRaPBYv6X3vN5EtI7T4\nyMplmC+ara/OY6F19fhqQQ7vwhpoOZ834u+FnN9tNOVvfrmb73rkTTidYRPR\ni5LD\r\n=QDvz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1HJtExiqdYB1xslfW0LoHB/Yzk2PC43XmAzTXQfNVWAIgDlsjqkpJrAYvfwvw19yA8h8q/RUdFmzKkWvTNUYpWe0="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kikuveya.0_1607726561347_0.22972880704808674"},"_hasShrinkwrap":false},"1.0.0-canary.kil5u21m.0":{"name":"plumier","version":"1.0.0-canary.kil5u21m.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kil5u21m.0+a047a9b","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a047a9b31e98138e9dc77b713e41dff1c13ce58f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kil5u21m.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-yvmEVl4t6N4pEFC6D9M+z62fSE/sch2dcFDqj77JbtPdc/XggxOLH5GZTpX4WVbnETdg5g+tstYJ7vgh54/wRQ==","shasum":"f6dfd78c10d9de2b3ed229a5dc5b7259f94809be","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kil5u21m.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf1D3MCRA9TVsSAnZWagAA7s0P/2quNQY79lTcBo6Wb1+h\nQ+YUIh5bmr8W9xOASGxq4ChuRfn/MpUroF+/Uu2CCB2pufJcs66MJVEQ8qbw\nzOsH67H8sfSDxYNLX0B2MYIQp46JPSLYFTML1xFJ1VGLKIvTlPTFy/IEtPCR\nQ/recVbZBdidkiegC6CeyTjtFi/FCpC1POP+n196gSAqVnboWkIcLrBIAaVj\no2b9q27VSqM8MnpH2awkYBdHHYMF6bqM238ZsNjLH/7+4SvAM2bXyuYGWSaF\nEqTjCyC3DmOGNKnoVBDidgtnHPJLpuuYVZ7uxZhmoaoWhUCSHPg7ZhHHuzz2\nNfh4jGux1MXGi0SCrvHUXa9Ht/ig8aqjPy/odefj0Naff1qXn2WsZuZBVzRO\nQDee8oTYffD8DD4oqPDElO72Yam0LEybsup2gplbjj/UkxKMACnxYPy/s9Ro\nauhV+qwmK/SC0FNy0ea7qHxRuVE2iJjHOHTBEzKB8Xjh9VjHZ9sXONVRwkMg\nvIdFUpuyeqXGB/Znh16lajP4UcxCiTxU2JLy0uiBwn1/jWH8Y468DaDSshCS\ndpQURsIqmh4J+6dPNY+s7WsInRQkqpZe5jmxn/9TbzSkpj/70V/1ZMVFlugh\nGV24zoT4gVz8UsOmEpvCFgaQ3B5Dm/arIkiOL94aNuSwgVFvn4E0NIGLUFYl\nCbBJ\r\n=LoAK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDzsYcVGsICEOI3HlzgtVU/hwoZ+laR3Rxb5kBlxn0TnAiA2eo21Lt9lVoXn/k5weGxzxRoregV0HYzF7Q84M5d9Bg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kil5u21m.0_1607744971816_0.677779157006474"},"_hasShrinkwrap":false},"1.0.0-canary.kilfkajr.0":{"name":"plumier","version":"1.0.0-canary.kilfkajr.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kilfkajr.0+7c881a5","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7c881a5e2e37cf99904e2cacb31b6ed0684358cd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kilfkajr.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-k9O9yuL7UKw31aFioC1Et3fMtzabnHOyZ9yGatL7hOfv0Q0zQfl5rW+0RyJKXQUXNDkKI1LszvPLRElRBkV/Rw==","shasum":"54235e31f44ada6398e77574e8535f904e58459d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kilfkajr.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf1H2jCRA9TVsSAnZWagAAqsIP/10l9sltctqfOQ37DXkY\n8DvbPCW8SWZZfHfzm7S+4mh/lV7f/yjQ8+jJG9DEn7nJmQGfIt3r9hxdnHnm\nJx1T0uvwurzhOSIEkcIBRuyYzSNlNE1zX7r10tI6pxT9MYDiHqCnFTDAo7Kq\nynedms/+9TQRdA1prmudks2GrBtm+td/vdhpXZtXLwuW97wD9DVtHkljbSzi\njvBhM/PpWFVWmmx2HoztwyxnrbTZseTYiGMyZo7nYtImp8WOw6WslwqqzF3V\nl0iK3V0qAu/S8ZEpJI1cjCgt9rCnkCFp96uiELdHYmxZaRQm3t1BmlajLi0F\nZBKTpzc9JOA3jGuYcY+rG7t1YGPTIuVsyQOZwBvjqaxnAjzYWKyIiXe4+wGY\nulSlyzn1ot3pfTn8kFJgP4WM2JMyT2hymZv/9cVZpuKYdTMbZV2VpVWUe8e8\nOLg/G3onZCpSE7Sv9r14UNcgqs/4ybLDl/IDwZx/BhZmGboDR1Up857UoEZj\n471oe5PtU/CDrLjh5WR0AUyG9iS/cxS+dZClKmcYeMoVGJHafue/RzXW9D/E\nIGlkmyBQRODMASL1vJ1UvxL8RO3kWvUzCrSCCK3pqqnzSC+7CtxOXDWGl7RW\nFvFGYhFuGVbBRVkSqCnt0lLVXbyUJd0b75DMmAJYIoMWjV3u6jogiUkgdN4L\n4u+K\r\n=Zgwu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCWARKQe1eNLUWElLkDlFpvd5g3lhc9iAMVslOkUuUVUwIgR6vRMLOt95jpzhnHyuJablDbdi1o6s1C/scluaPvZbc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kilfkajr.0_1607761314842_0.7125874136724544"},"_hasShrinkwrap":false},"1.0.0-canary.kio0koex.0":{"name":"plumier","version":"1.0.0-canary.kio0koex.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kio0koex.0+d3d234b","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d3d234bf98c1ee5dbd8fdd867c292d33f74f7ef7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kio0koex.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-Bb+qplm2RY3VkPk77VmfIINKz36ucUHWfWfpO62/uwYQUREzjSgG9F1wbdeoQ7tmWCsO3m1iv/q9jYCX8JEqig==","shasum":"5fcc2bf8536aab6bb4bdc08c46d798d49ebc753b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kio0koex.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf1t/gCRA9TVsSAnZWagAACfcQAKIlwuaH38u2Xmox00hb\n8l6Un3dFkFbDNzNIF0BFDl60qMbug7/dUcnZSH0X4EHVT9VLCF8crvj1dxiR\nm5YPn9Sg8u8B+lPcfUjQ84tn+uIvAJ0a60o5W+HtIeccA8nmok6mUJioGx+A\nZWKjL/CDDuKhMv5e/tb341jHQ77jBQ4+tXAQxYY37lnVAwBUYO2Hggx7fTdq\ntOYIJDRkAkz5A58sFthu4TwIEsOA+XRFEnfPR9P2F6RvLjW2iP28FppST+Ki\n9Iu4J0gXkiyGouVQTAp/JmUFZU8CvGHhuAqCMtfDl8M8kGJXBQsanypXoNzY\nqdREaBqlpNWpVd07PBbe3n2YkrSD+1UDjdGBa//HAANmDOZWn8+7G6rhMpOx\nIDEanH2OG8lMd3MDo5fcDfqYnG4oWEu5cNr6dz25dQYOOCbwN3NzE/5ZFs5b\ng2AYxWBWfZd9eoRu5lg13HL21E/k56NBj18T+I3cP54oE1NIPc1tCGKIOnGD\nXrgbCzc7ypB6kDXBS0rAHAXhPZ/YytZZXZdvSrQDJS6Rk+is0Zfv8Y75wu5y\njqW75/vulCcK5coxG+JYxrbbRDw067eNl5MXDQxZ35gVWFXmQ/LUEFxVkXut\nfJnvdElPLP4fWsOlvMSBD9xSFHs1DeDeIg6KHDPbXURk1rML5XrIvUfIs+rb\nKPuR\r\n=50MK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH47N8ad5BWMuGwV5g0HEyTn2sYmB3JKkxPBTp9zN5NNAiEAo1sDwlxVKSI/r9zw3YhjfsPnep2oKLXdtHIVVdUhHJA="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kio0koex.0_1607917535570_0.10721572406171465"},"_hasShrinkwrap":false},"1.0.0-canary.kio5kror.0":{"name":"plumier","version":"1.0.0-canary.kio5kror.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kio5kror.0+49b1292","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"49b1292eaa320cae003bae42cd427f5dfe9b1277","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kio5kror.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-yH4p6LL8CAi8w+TAoNbGkFvfE34iUW8xOUpmO2PTBFUSllsk49iBoUuYJCdqQxnNrL43R3gaoZWb6/KNoxErDA==","shasum":"3361d7e4249382ce8b0938a687109566eb1b4ed8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kio5kror.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf1wC5CRA9TVsSAnZWagAA1z4P/21QYUcma7X0C5VkeCif\n+pLfJIlubRbrZWBx0SiLcXkdyal5rqEJ0HYticipYzXngc7el/s8FIMDHH9v\nBVsRyPaQM7lrSGMMMXii5Zl4B85NB6X4rkWtN2Jo6wgcxxk2o/1pS7f0rpTn\nA7x+gxxFnm6vdGP90zBaunL73KVTSEdeR3MydmF7RSgAJk1OZEpcoHzqx38/\nSGDPBHHAhF/nJxsfz2m4U3wXhpWZsd5NJeZgtyqfLSjwrow+HbKiyAjeKfpq\nu2MG8ifHXqu+Td92YSNtEwAO7BZ756Cf5Z3D94iX78/uSkHqeyhb8dg5ta20\nIcUvmCDKJzBhaUvByssbjcOjovJnF+45VO3Z+yydFXr+qpUzlbIE8PxSMPyM\nMgILysFA5nZU6+1ys5dCVBv94Z4Ad7kgrQMGTXU11WmYEnhHy6FrFCSfhtER\nleXg+oyGkTog5x9V145iFGxtg7x7cJ4myIfh0XS5B9w1MWiPKDvf5rTSFO8K\n0uwURMxo5y5+VEKtGHO2ljMS6eO8ODy9Yp8H0FqCzMQyWUA8nSLnEF5+y0rz\nDOkdcu/9vxOpS5LZY/kjdVbEA80d8fgUuNO7+VbNf3CqIqwYggFLQfC26MVs\n/gGkV6LpU4Peq6WJ1bmpsjcGyLCSxVC8Ie5RmCi6WWKr1XkS4mscj9EXQEkK\niXHe\r\n=zSHG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjBo7ExkWW+4VevbkXGOfHAS0WTsmVKwYD8FMJXZaoTwIgbfsKsf7m5Y8lEQ6lrAw2rKytClboXe3jfZ5Z2ssX5WM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kio5kror.0_1607925945495_0.03414892460777841"},"_hasShrinkwrap":false},"1.0.0-canary.kiql6xtk.0":{"name":"plumier","version":"1.0.0-canary.kiql6xtk.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kiql6xtk.0+659960b","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"659960bac71438d2171179879699a538317b8f88","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kiql6xtk.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-0LZew5hkfWOiyE74BihXeK7eVKCmxQjYxhk8JENR0EZAQFfThGBwhC8W/IN1JBJz1lNC1FureEVYydTf/mu8XQ==","shasum":"8ec4e84b0e08121758626bb15fefa61172c6d16a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kiql6xtk.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf2T+QCRA9TVsSAnZWagAAs9EQAKRxlKftHVkAzVRuYaAd\nupMfFSObgQOsF5cl9E0XbJrrkoP2Q2IeyOukNOo9xZBXSVOx64ULmBJiclmb\nGMKmsT2L97yFjkrwVnUYw28pRwtVZ83f3RDV4Sa3sVgpuJGSnriG8CArk/il\nfeETyOCzdFmpDavZwv3driwFMV+S1Cc0pK3S6PhTQNTg4A8bVj4GpZiEniP6\ndChCiFr71eTmhxSlnhk0iMEfe6VGpziXPv2WQUBQBytmooqhtQkf9urfapzl\n1iP4GAtLjxKPwR2e20W/AGjqo1rjhXkGJlHEpLZ9plwLO6gfP0Ep/Y+vUFjg\nMKiJvqlJUMC9tWVc6OOE3332kwj7qUfuDZtztjBi1d5k9LXK4Q5sjffZsu4p\n9AfE7TXl6+En7RG58W4b3+YEGS4rTX4WOvjw/Xr9R7cDnJ6xDChxq8GnM8dy\nEO3B+4SrCCYOSVBahQ3UFgob35IaBWGeWgS1/pGAgUfGWpIAvPmcTzXgWydE\ncfD5LigstrGiYn1yQAkmRg/bHVqQWplB1i4KeBJGdBK9TeXSa9KyYWMPlI+D\nx4DKwLVD9yMWdO/9h0zYfjkpo7XTJSWGDmXlBMdfjKTAa+GmNGJl7ZYcLQyC\nasVpGFCZo8Ibp+LK87VtQ7JG0rz/6m3cbR/AyrGMlVBeR7sRWzQsP82q4Z1g\n/zaf\r\n=THO1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGSbzPRkJDu3kE6eKVyfaA6u73pgcf981CaZzc5vXCmxAiBiXwncCzXAgt08csrWrPCFF4bvmhQCwmdAmNPBhBjbZg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kiql6xtk.0_1608073104267_0.7320934080848951"},"_hasShrinkwrap":false},"1.0.0-canary.kiqtyzoi.0":{"name":"plumier","version":"1.0.0-canary.kiqtyzoi.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kiqtyzoi.0+b14f228","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b14f2281cf61e49d564ecce2945ec86c84cd5fc3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kiqtyzoi.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-X6bqkpWC9+U6BGPMyuGkCqgmgs3VFmAwawKHmtVe616IO2we/1ijVplC43z2V76eUkdrPhWaY241nU7iX3G1gQ==","shasum":"764e1ef59bbc9aff1f733222c729801f6031ae78","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kiqtyzoi.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf2XknCRA9TVsSAnZWagAAC04P/R9yBvxD69z+TuDl/xUx\nR/838cePYN804jkyeqjV0aAUQj0DsBYtkmH6xon/9701gzVHve0qT+NfkUHA\nBsLNcm+8160nyMPKg6Gk4YoLFOlEztIUShnj9irNfarV100zv1OVfT8w8o9g\ng94A+dMlEvtRzgTwf/DAaZ8QOPlv7p9/ocj1HPyCERTlsaCS/cXUzlNXEDhu\nUDrYneyHAbuGCIIBR96jD+z3ywnwP9QdGm2MJCGhI8PbxpIqfmzmnH9Q9idN\nQGqadDN6Bmezqz2Re+8cbOorjC2q86ZMUYoDn86H/4njq1vU8M/4LS4NC/9r\n4eMrSug8+GzfZSGIkddqlvb295FckRkZXTMdGTvISNXI0gfq67Po2f9alzvd\npAfvj0jukJ7eYCkG/K8eNpa+/BN4dlAWatFPU35BigU3kBiVwzQt2gY1aiiW\nsRtuswwZlmCvZcVSSPxZCI47LbA/edikOFoBzA7uGfUx6PWnOvEb9ojdc/B0\nYn7O0prLKNORwEuax0WFLVHxFyf+YBCOEYwBxMLlvl2NHr7NE3bSyEnaHHHp\ntkyFppiEYs657hYtPHvaJcARtX4pBopZqbvPDCSohQb/wQTa3JxKcwZmP3WV\nsBy9zm23f10eW1Ay3lfwJR5wtguIvUf1oXPdlUHKRj9LSepCA88JVVYMl/AX\nErFx\r\n=rb4M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZE5bCz+2bFi6w9XlbG/BzGOu4tTMqGsG3V1u/eEXfrQIhAJP+cAjfrc/EFIzsy6pN0LyLwTaGGmwM6SrY//arWksC"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kiqtyzoi.0_1608087846704_0.413568395854748"},"_hasShrinkwrap":false},"1.0.0-canary.kiqybylo.0":{"name":"plumier","version":"1.0.0-canary.kiqybylo.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kiqybylo.0+8db3d71","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8db3d719954aa70d98eff68f138f6f26f92f28eb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kiqybylo.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-m75lXdgFKXoO7I10PgQD3QuA9NMAMNCwOA8pIPBC66uUBtjgqV8BjZ17TzOWzENjPBstTcUWvbYhIYyyZybqZw==","shasum":"13e35464bde51a581a7ccf0dc742ede168744bfd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kiqybylo.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf2ZXECRA9TVsSAnZWagAAhvYP/0+2Z7UD7D/Ze4jPHM/2\nouBUo1nAR8NBmPPNQ1NoOY7JP6rC3ZZHUxZ9uGWWWgy9TzFBqsoOxi54SMjd\nLDKQiJQ7NDg/M7Q2WiZ3bQMc8GRq4vrG57Sd27mBf2UeR5R4QzhdGV2LMAL6\ngNXin1yZTDES+KJqZu7JviU6okCpjDWwX9TLQgk4n1uE67CiXoR8+Spc7JC6\nIZswQObhCrBkRf39foE8fT+PqOXyTYJ9gnIvSDhxEy4ttP9JCNThav51g28f\nfKSZe/RtSKB7R2mwmd172EBqeNILzXDHQnxPjR0DPkvPnIca2Vnj8+yzPbJL\nDm86UMH9uvmjG3euvRHYFULUbStEsEMFo46UMuw5sz7iDbHWoz58D9o5fztR\nVlERrwu8NYUUWu0IT8yDw3/LqqJLU09uW9wWD7uf/huJIGHuRJgirdNpaEbQ\nsvpapohsK3sXcVZ9N8G8vJR7n6lwfd2Cm/sRSy4gC4ib6BXBBottyDBjOERu\ni8AqeBwdtDvE13Z4iHLkjECbRiGkgcQJD6McqAze11F29a6tz0EmIGAtkEpH\ngPvGvpjARi6CycWwFpEWra+bV4U/I26STMuskH3OYc7W5U+HW5+l9z9/S1D9\nbbA7fqbRp+VFWQb1LfBXrbbnaeVc/KgA7GjUMx+0pjSkdlpl+M+2Z0JKLgDE\ntT/I\r\n=QazM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICZWT4rWpPXrtS7ZRfkmCFXGVai7CgMZLehwgH9lk/43AiA744j5mB05Qa8YE2d0Gn+FsiAlEzO8qbi9o1dy33YCCw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kiqybylo.0_1608095171965_0.617764586643379"},"_hasShrinkwrap":false},"1.0.0-canary.kiqz68ge.0":{"name":"plumier","version":"1.0.0-canary.kiqz68ge.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kiqz68ge.0+f69e644","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f69e64435c2e15c1c8eb111e2d00e6913dfbf4a8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kiqz68ge.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-HhXYws+7SZkAivL8OIN2nn+VPTQh0dze13i27QHfI1Fm6ou9JG7JrikP31aL+beiWfNGoLbfbyIG3r8p06hXDg==","shasum":"ec23f4d58997b208779cc203c56c4403f325d5c1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kiqz68ge.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf2ZtKCRA9TVsSAnZWagAAW24QAJwFk8Gf5O5DOKeDYtoZ\nElw6e7Assg7i4iwN6cZpIORhMzm7/rMkJUFFBToyViX0grEDS+/4QW76IFk2\n1j3jpzCAb4ZobtLtYmVpwfQ8mx7MqUIIUCRsQd5vU/SyICMA1YIrZ4tDZDcP\nFLObWsZQlFVjQ6jffhPqROuvnLD5nit32GShwyBkCQsNNuqcxYXYC/5KcwAX\nfZYMk0wIA9EGO/y8U5p+pfSZs7/j6Kv6X9ffWmXzLpMV6wrGwszx692sSN3q\nAP9lcnK9LFIVdFQ/GZdZzjK9itgF09me/oXBBL3ttKT20rS2+kuVRdQSylSZ\nCcSDZ1MFZvbKkEi0+Fr4oChWJJmhGJFqm/NlljmqaG8V3ywt6o6LOaPmf4gq\nI/mlySfJIKiVk7kVXqAVpLDpafu/cOi630BF/2kFP7GUL43bH+qMERddFSVQ\nh4CAYADGPrQLWlrSTZepNGxIJ5DHEWdy1GtxfekVBDEaKVLda2FaAmDyTiIM\ni/HWcc/wh09KeiQMFhK6WHPOWAHrh6AfXFx41Ee7/68+YHGFWcQFEWsi4NmH\nklyqu47E1OBG3G4lnakwy194RswWTrBmIe8ky8YRZjXAv8h+XiVCRNPfeD83\nkurx6RQEZwnOtDx0MHrceW+eeE70sH4olXg8eb/h2MLfUCfY2jdJIJao+eCq\nC6li\r\n=mH5T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQChqyuEWH+ofwTj+r4ZM6TjaZGBx8I0SO4Fv4bFXzfOPgIhALs3qjbBsxq19Mj0utkVP64241+xEhUQ9cIl6Sa3tNEU"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kiqz68ge.0_1608096585538_0.8805781378930233"},"_hasShrinkwrap":false},"1.0.0-canary.kiutiy7w.0":{"name":"plumier","version":"1.0.0-canary.kiutiy7w.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kiutiy7w.0+f8ba20c","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f8ba20cbf7e9ff98df352b7377625e267a7a59f5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kiutiy7w.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-UFNhCEoUBVo3XkxizCP+iTdg9dAg4JwNlDDvsi38YD6Rbz+2E75Yis/H+avraI/Zv5JBz58iyu5LfsDkrsRtag==","shasum":"9343ada8cb7e0a4e4a40f2af61caaad78948684c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kiutiy7w.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf3ScBCRA9TVsSAnZWagAAomQP/iO0aXYzyajOf0qTpTN1\n0ofQ4TqmEvvetRIq13ns6i28XZTCOZF+GlwerA3AMm4GZ/CjjLXa355/BWws\nwfwf/Yq5iBI/oT6bN1KWe22VocCtahHi7pQFda5kQR10gAY0R+N8dhp4UVKI\no/CTDhsTSw1nxO3MQ3GS6yDjrogszXb+FFqOX9r1adWwG35tVrqtr44wM4Xy\nh0//l0+Rg4degmjxseeJswAWExvtMpxDGkjeTiDAGLK7uAYMToep7fC50vVU\npDlb6bIIpOmupQkNmJZqhkEWfQf8RU7ZzEM8EHEd7aZmewaCUool7TWdTnTl\n8De5Z7ie6eCSFFv2a0cxh3SxqLdYsX6N9tTSjS1C80jU0kXGGQVtY6uStmgc\n+vgCxm7+b1sw3MDEGa9BRxTSABGQDXYktxamYiD1mZCGXTdr0p0uS+NLXcpQ\nY3lrgZHiPiLHcXLVKDwdVep2bzgSZLfrqDm7/XSepAyDI7XaT3CFnG379mNJ\nENo0UWlAD//rW2MStn/noV+MYfYkMAgNG2/kcJbxS1mHcr8qlvwuMyMpY9Z0\n8fqd+mAJAySmcN07/BrBf+/uL+Ndpw/VnXFri9nwbEhd69JfpbPW7rfeczuc\nMUfHvVrqIdF4mnMkpSMri/li/jyZb+Qize+ytrvwtHLRH4SaeFdRchFIGLNW\nHiAP\r\n=hsHJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUyBmkYdag5yFyecsHeHmfKusNyuj5+YbNxLFSrKeVaAIhALyRz/ruTMUNf8UehtEvXRM5dZ4RtZk1brVUb9iV6VZm"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kiutiy7w.0_1608328961187_0.8135528405372441"},"_hasShrinkwrap":false},"1.0.0-canary.kiux0iyr.0":{"name":"plumier","version":"1.0.0-canary.kiux0iyr.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kiux0iyr.0+b504093","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b504093a829282933840f4746ea0c1f97030e0b6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kiux0iyr.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-aPHiJgmZpmo65kQsNyVxIMiqXKsC2nvdGs4k7cxxAVwh3b/DT92ocZy8ru25SdI8/695SV3iqr1Gk4H6Zp/w5Q==","shasum":"9af3b98505e6372956ff99b88a9f5af61f992642","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kiux0iyr.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf3T3lCRA9TVsSAnZWagAAwQsQAJ5ZNcvcg/9W7/z+0BRb\nwYYEkv/T+hvUR5JBjlwn/KyNQfrm7mQTceWk7Rx4n3XUaV/gunTVmNchBQrY\nBemIn0ailxXXwQc4ikHTo1zDi9YHuPRQE5NKOtwbenTlFIcFFGAfMKIhsj7u\nBHF7De2sCTq9ZNc2+FsAISDJc9ztEQXjzZoLGn8qMTL8znvi6iE1dv9tuhSU\n4wz/JrzA/xIIRhHRtF9I6T+KOPrk6xQqMfQAo3XFeu09bo5a2pM86b9IUDZ3\nouVvPc7Mflt071PQD+QMnr9Sqomv4cNfMzd4yIqG8EfUvqaVs+sfx+aNvw0m\n1UVyd/ZLzM+vP6scvEO/Go2NLUkQl3Y1sS2AQ4nH0ov6/pEeDTShEJGaVARX\nGavpEhvIS3qgk8+XKM5qcHZlsbmGrcNPYR0F6vnGhGcX139jaIdtIs9i4l4k\nCoJLKxTNjqjWffXDxdZ/358NkVQJLZ82IFlONjbsUBBAHlVMoNHnLeo1uQZ7\nwWkogM/LMyeytr4fVuYj1uSfvePG4CiK8L6xswib1lXXvTZBz+pd2Gyvk1Ac\n2QdWCORZLU7w5Y6Rb5ybFqvLOVB8PT8zWSjgiYtRWP6ev4uQsHKhCoHAM5U8\nt0SbL9zp9rpwp62APskDJXNZ4L/zWqeiRGXwHkXSimUkqeb0cmvJQgNqb1BP\natkU\r\n=Uh6/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAMRaosQH3GOJhrw54GrflU+2+poRsaIT2SHjgqx3gRGAiBgKr7ZFCIsdw7BgZm4gTllym27767h8FXsEpo2Jxf6Kg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kiux0iyr.0_1608334821318_0.013934330443114762"},"_hasShrinkwrap":false},"1.0.0-canary.kiybwviv.0":{"name":"plumier","version":"1.0.0-canary.kiybwviv.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kiybwviv.0+516adb0","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"516adb0f457e1f7e70035a5213ec910b23950767","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kiybwviv.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-OAFd9WKU0luO04iANNblfziTWAyxkN/2+8jRfpDPn2iHk8vollw6M3EqORBTR98IqTMx94WK47uRVyJFrFY2dQ==","shasum":"4fc7255d46f4bcf91abd8f24288317cdefe3cbfc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kiybwviv.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4GQ7CRA9TVsSAnZWagAA51oQAJQT+S//l3UQvQ9RNAfM\nXuimREvNeBWqCMTeVgOcWcbzl6c5aPBlXteq28KzjcbknfVT1OmTLKzj05ma\nwKhr73yJe6ZmwX6NInD9aOZKnaAhuu9DvlR+cQ0CYZD8N8prr7mGfNdJ09Gy\ny2G9o4qdUrRHbo7bV/ILsEX47lxhXaPMYjtbypR57ZmTMw90Z5MJH3atyU2h\nd+0O9Iz3Ec/K9ESOUSxoSNshc1W7pRQzE5FjoejekmZIda9nU62kC0ua7eUw\n52jONLYafABHpzOJ2pisq+SrsNQzmQwTkDu9OmAGXf8icQFGLnaF/VwVkeJq\n336QER0nmzMcM0X7wNZ5HA3DERyMFfeA+OagqmknJQE25ECODWaeFpiYJedV\nlv54RMBR5vieQB7JjjgH2/Is1p6pCzkAjEr/1vpL4f8DQvX54wT5MlBfSxKm\nCuCzFb+wJy1KCGwXKHZBlld1H8rBJA4zlz8o3kGXmzjXK439oNz96k0iGGyr\n3ytXOfRi3U3tqLPPmuitc3CWOLuA2pIBgSA3y+FldUQxQk8HcpidNd579W1C\nK2VSe2x6Tpn51sBfd/ftsV+yVfSyxDDUuo04GwLbI9ShY9c89kuL+5f26TN6\nTl2ibRYgW8gdNaspx9wb96NrJSQkiS0str6ZYOxAbH0QUd49CUDIRG24Unt8\n8LAL\r\n=/E1V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDOV+sWvw/SeauxYb5VIjQB41ALBG3T/4Wb26XjU+hRpgIhAKSiqJPvVzsInLS3ciLWC+jWWCoPnEHfXatq3sKus6I8"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kiybwviv.0_1608541242825_0.8847438276913662"},"_hasShrinkwrap":false},"1.0.0-canary.kjaisf7u.0":{"name":"plumier","version":"1.0.0-canary.kjaisf7u.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjaisf7u.0+12c4be1","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"12c4be1bb5efefc2cb1207475c68f533bbef8744","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjaisf7u.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-kFSbQvJm+ZqecOgut73FszFS//rvi0CZJG4dA0hYbG6q+DQtzomI0dCUDe4JW2Q9NW+CWOPwC4Xi5qR60ocIcQ==","shasum":"ec19a6dac2e613c99d36efd95a02f073b47f4215","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjaisf7u.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf66O1CRA9TVsSAnZWagAAfjAP/A5fq84hDN6Z5jujX5r2\na/58b3J4tHf+Bxjr/is3wocBDj9XvM9a2kX10J/qcyXtwo2b8n/rz5aR390M\n1xd3M3p8uh6fkFAquwwZPHnli2skrPsNpYfWBvDca53tYlnNZSVmisMrP1VK\nRElWE8OrBFOCgspKvODKkioB4BU5AP+f5bRWfjUnuEwHqXTBY8NM8Tb1KoA9\nBJghGfj0j+l5kq6e4lKJXwDBsXVr+zeKnNsPiDnhnLIe18lrxsC/edfWvDAM\ndYDCj3ya5Nh8b4d3J/cNyetJWcUmQTv09QXPi0IVTeeKKSfB5OIR0cGPThPu\nODNS93zCZCwr0Sfjipsv3IMuIGC8h/Rg9xKZBucw47oH1Xifoo+ueEdfacKD\nKZxU1s7vLW/cyKDiZpAXyjkBSTv2MKshb9uNCTcPzz16+CofKtMipQi6mjuV\n/UKlPTIKfxNDAQwNtOEHEIGyEo0hG/naEp7HynYaRPWvkZSzBqUuRonWiVaD\nd0Sl4O+IxjkKRkScLmn6HIPSjmNRyK0tHx8wtprBjzbk+JD6UdxRuw4y8eVm\ncs8aE3Q2H9cXPKIz8V6Yn/Qjc+q1dGGqtFW2WNCDWypjJX97WkJp3/LLogyp\nwbGvbYuFqXyoYEBv2FJ9LhugYPpdTPYDcDtMUnvk5jGFUPFoadB1PrO7UHn/\nsp91\r\n=fJvy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDHRov3AJLqzeU0LMPTt1YlLdqe4ORS2Q4QpOfTlF9RAAIhAJt2TfOriKIGKLUIugqJilt95gMOih4Pjomk2f9EF6Bm"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjaisf7u.0_1609278388664_0.1390730736391521"},"_hasShrinkwrap":false},"1.0.0-canary.kjjm44kp.0":{"name":"plumier","version":"1.0.0-canary.kjjm44kp.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjjm44kp.0+4d80018","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.0","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4d80018afa3e4b2e8dec594b2bbc88fc08ed1038","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjjm44kp.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-hJPAaj5DstyzKKDQZsfxGBbPutZBemvQsp0oJyXnpRnJ2Ss4CjjZmw2bA6+LapeoIGxXiMhvXvM/MBREGX15FA==","shasum":"8342c74f0ace48db55dfbbce5a12ba640e33d6f9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjjm44kp.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9AdJCRA9TVsSAnZWagAAZtcQAIfGb8VTlkgMdZWdcm7t\nA23s4oQhES9T3sPVNKREsfgDrPw98TnsfQzJL4+GQqV6TFNzF53SvBLWL4zg\nk6W/OgdpfyF0ajf7/p+AhYSxGrXB8dIAgIGcoHXHktXAx3xGI24QNSruH6mZ\nRrL4/J2KgpWhItkizS59jWrR81hLOz3CpFb8jVftpRNzf/I/Opf/qQa2iFsF\nQGIPgkzBrrr4Sd6g1S0oJC2qjXLTcoChzpkv4/4pZ/2sR5+TBshm41RrdpsV\n8BAe9Ag1YrYW+uQy0BhD6l3Gp4k871lHbRUPUezFt2OaPss9CfiF6rWZxxDW\nedxOdR8EKphtR82ated1xVMl40xTFAd8hheKCPquzWUwdVv90pPrulCuw4YH\nNrha3hd5k9iF/mh8DQElt7XQ2xPqn1dpkn8+qUhQob8KPxQP3cApQnpi6dEW\nIicUMEiYaiVoWvxlfh4gdm1vCTd/tIP+cxdXHg16pViB+G6Ayw+xKlAHTJNE\nlIVIqxMPIZgvC6TiAPr1M6bPWQg5vPleD/VDWfu1iHuKSG3Mb+GXJf127buW\npoiK5WHfs+zo5uLSWe73i4wHSpVmLhU/YT1OOdw2bWZteJygderEfYlrPmQP\nIreoATyJXxag/g6LYpWjKsiIv+dZCAFvC7gxu+z+vh4RcvNNQ3pdUzpincmv\nOgSd\r\n=AEz2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAqUlUcUloyJUDGOZ8ekeG1DbFzQbdWxYW8IeSPfIYfiAiEAiijvFRsPjCn9tn/DRcg1HPUKpqvTbGrliP/xOpX5z0A="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjjm44kp.0_1609828169016_0.37491265532160334"},"_hasShrinkwrap":false},"1.0.0-canary.kjkhwbz4.0":{"name":"plumier","version":"1.0.0-canary.kjkhwbz4.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjkhwbz4.0+664837b","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"664837ba78eb293d865c3cbf449154135e7ca63b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjkhwbz4.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-VOPZoo9lUGP8IS0Bw/q2JeJmT74B2JJoUkmz9TVGSUYY3DJn3BzU5wlq2eo8JhaeGTe12FX73dlB3BJWC7d7Qg==","shasum":"c27c42ca930e713eff195ff3b08b3c14dccbf359","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjkhwbz4.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9NfUCRA9TVsSAnZWagAAOUQP+wanlg6Y3NFW2isnhJDA\nMDxGtysct4DZS6BE+rQwuQ88TyJS4Wt526s2PunH5lsynIIN1utQB9Y65QLf\n4kbxvrHoF9ugi4J/0ebeQLeaDWhi6hA1HLQwmYIFaDnYOjZNQxGclF/pESZp\nmSN8OKKsanzQXKPMsQDzNPD9Smdw9DiOIbNtl+DF0Zpv7Fuc6/Gy6LwMO6zv\nM4+SAe9Zsk8baqU5Ml0r71Pd5OSWoDHkILSNdgaEKRKr8HcRoDG+azMo++Wn\nE1V/RKZXFhJZeB1vjTuO3rdPeXJDk6sdnnJf/WLmNWTaWYOQ/F5XAZPMvJna\nncyLr1KNmkz+a7ZF9GvyES9+y3NLLrYf+c4J+N54W+/ESPrCKfE06daIMz00\nKu3PstCjvkjPQr8YTCJvd2ndywIVNCL2U6GWP0AoQK/nG3xkYhC9xjjl29lF\nNO96wotvOZuHNUbecHhcHF/cokvjgAvvjiZ4J2fEi5Q/EPQCYVARPT97yNF2\n+FchlxOhjuAFCSucQJHZODe5ovVlYPbhPMm4B5c04Imrw3rDRLoKiMCZTlRM\nHHo7KMy+gTqFGp0KAbzGirkCQ6XU1y1ivX1ChHAzC29mKjcZ0R6g2XkGIoRM\nXCHXikp94LIjZTiRt8O8ouAWvtBmISIoUiJmfcUWabSTrM/YegFAwwzMWVUb\nQnhY\r\n=yfRw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBlJyB/WgvYLcycg9Lo/qTlTvcujTmU1xc+5ELc7gidFAiEA/MU5tNY7az7ceGSXjsUprn8A25XeiOq0H2bhVBKAVMM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjkhwbz4.0_1609881556186_0.9783631837001856"},"_hasShrinkwrap":false},"1.0.0-canary.kjkji9nf.0":{"name":"plumier","version":"1.0.0-canary.kjkji9nf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjkji9nf.0+595472a","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"595472a760543d039bc138808cab9466bcc28fd5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjkji9nf.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-WD3huzucoL2UhFaPG0TW1shm/iIjBfH2Cokl5fJC2yUNDYrxcN3FjFK2foJngHodUd4aKNXwn4GL9n4VP8kNGw==","shasum":"0dfc4a3d307eb1c0bb13980468bbc2ebfe2e9b51","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjkji9nf.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9OJhCRA9TVsSAnZWagAAG+QP/A3Pr/brGKcsBSR11h2Q\nyflIhmIvwWLcAAB5E1UHv4x9jnr9nZ8bYymmiHnnzQTaTnxZ4pPJdGcVt0vE\nOv1cCU2zlsWiKfhayhMp87nTulPAtuKhu6UYGPx+Nsa0nJwf5vSDVx66SIBF\nJozmliHW7SlsDsBj7H/aF6f712kyJCuKC52e+bjdxH1uLVIxQPmsyP3FTgGk\ny32MM0dxGcNSmtvDvCRWdYUpt3q2Or9e0Un7xoDB5cGLD91lin0Wuiv4rHDP\nCSrdF49i8/hMTWcAPDl9VY0Avw2Bk1Ra7Iil3BgpNNkEAXzbAvg84qoXVrlZ\n9W+5MkyoNoWeLPyY5BzAB8mgXhGKOwMK5RjrXO5DEMzE3EHXcqls8AA3TnBw\ntrBZt9qCU/4LLIn2jNCPRsoVfbUWIdtC6nJbkqoz+E/uYedqcQrEWSBmLuGF\nQ0vMOipTvSskymhywviPULjxtp3cdEtFcNijgWW3u4B+BTg3APoH9wkDM8qm\nioBavY82eXmCDXqbGyOMa8jgDyPgF4FDfU5iO+xp1pOBo8UfHM5BPREBSUaG\nQCQjZdnGerNjrd8l7d/vFW4T6ddVtcU1w6GgEuIdfzFEeG0Vp2GnA9R/VWHG\nxVQnFkIe0tlSSIMko/daI626oCLeSJ934jWSdxXo3ksEp91IXrqWNOrcG9Hd\nPOgb\r\n=tKpb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICNE2Rixm5z4NccfRMIf5XsurP9GlssxLVxTf4O7POIGAiBoMFj1KGOYdCrLt5aIxpCsvM9AYholsxiKHd7VkzSPNA=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjkji9nf.0_1609884256639_0.3761303690147435"},"_hasShrinkwrap":false},"1.0.0-rc.4":{"name":"plumier","version":"1.0.0-rc.4","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.4","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"59d1669d9349243ca9d5d014b102c2f3b1dfbeae","_id":"plumier@1.0.0-rc.4","_nodeVersion":"10.19.0","_npmVersion":"lerna/3.22.1/node@v10.19.0+x64 (darwin)","dist":{"integrity":"sha512-6qq7v67Sd7hpsUDj9lUZe5jTUnADvCmr/ZTv0wCY8CGzHd4vNBdKjFYBOi8c0cCuYeEh6zlk+08jAgW1KvKpPg==","shasum":"22808120b5869d47a029797815d8296c8a00f294","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.4.tgz","fileCount":13,"unpackedSize":34094,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf93HcCRA9TVsSAnZWagAA3iIP/RY1AkGi8x7GuMcg7edt\nwaUnbP7/ROxdEcXodqD/RfpUAYkBwidzs8RDszpOTjBivT/7B9l2997X132z\ngjfgWKgfc85pW8EWMrUtv1OtpJoA02YdWdEQEOigDl2IFfWex9AqDf45kQEz\nrkdHy3FPXWzcGykdM9DcRDVKAbGpwydNTKkLkR5TlAvYYAzde+s9fDuRcmm+\nndAwIADsuefL38ofbiFbM4jMBFHAdJMhYdIedYHCrbvUhvTTtsoxwmAOgIpq\nvQps8BOPwgfFxOpnir7NSKVos93xLWozc1MYSshAEV7/waK1E3NW7w7q1eeE\njCNxAwFElKoSz66aO59hxUSAfSRkgzXe6omHFJQcRHFEwPCjDeT/RPGU1wJG\nQQlRubIlYgjjAh+7xHyJx0kBHK0DEnXrvVA1UQRERvqQVDpfSjgBkUgwXifj\n++z4nhdkItf5W4lcZqCS8fmgs0Ij/J+92afM6lvuA6QwjZ/BucRk9RLsJw+q\nTmZjdT/aXvAn2TVeOJ0KFhB6Yt7rlG16jn6+qx4VRFZjWiv6yvw+TSAzfAdE\n0NqBTnZDVeealp0LQXVHKgqCvTnnzJrmiZK8VBMU9NOXViFLGslhy/l7xRk9\n+mHqkdTASqjxLcyGPytODsDIz0Z9JhnUqvl88ow+sLq5UwOJiSBn/6VMpl7Z\nrnGS\r\n=d69M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGkOFYj+FLwaiU8mPb355GsuNBgNTdqVe8LyvqQ2YFGwIhAP+BTxZwW6gYH5FpxE8oYWdYY5xJICgTZxdtSRuaObUo"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.4_1610052059680_0.030893531926069473"},"_hasShrinkwrap":false},"1.0.0-canary.kjnbgxhp.0":{"name":"plumier","version":"1.0.0-canary.kjnbgxhp.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjnbgxhp.0+0734d40","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0734d40773cfe8aa9192b0af58f7860edd769b02","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjnbgxhp.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-W9Fjf9nxQoKTL5HXSZMzI3ks2CDMk4aVZUjb2ItwGvjuGc/u19/QcYqGQAK3PywQc3irDH8wKIDbsP+NAYu1lQ==","shasum":"16eba1e96a89b8b89248d66472c7f5f2cc64acf9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjnbgxhp.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf93I9CRA9TVsSAnZWagAAVqYP/i1VHZ+kW8aex+5kSGIc\n/Atwutlu/+tUVCcqtjpFncD0zKzGcglxNSIlHsObuStU6HbR492OkTY/6kut\ni9gRnKqSq89wdqVlVEhXojkAr/S+ajLsrQmzd2Z4TUQvn26Wg9Y53MB0iZzw\neldZmmbgA1YCIXcNU4CiQov+PGGmupqcl4aHFgOlLWFYE8DnLW1SdioFtDO2\nTWNQp3o5JVr1niSLqLt0FiY0NAlSCZMnF7NwuMapms71UBk4lyJNYorRby2f\n+jqqaOoWwUp5bRj5RXAggul2F6ChNplEDqd3h+R3Uh1opVj7/mQ3CwPIRqCD\nDaOTQj4ptEIfqx5GEH8m/oce0ToD/PwSYCiPp9WI4aS3LBv157QHw9U0dwfW\nff8C1485+OJ72DR/J5O0HGkBitp00VV/HD0Iuz3YNNPcT4CJwobrkAwIAR+e\nky44fnbvxTZ2cJMYtllKU8sV40HftgBWrqW54jxsvzZGnlwvk0kQmsp6qla9\nSPyvULqf2cvQt3HjL2ROwytUGw8mCax5Zc6dNl1aEvmFwTs6XOTDVB+k6vOU\nPR/nEImkRuGLPNXGRSMsnNMj4PLUmg879O2CvDNrv31DH5xQnGHwiRqR0jKT\nDyA72Ypiq+dzpG4rgnbSk+LflhPQ9e6r0O+NZ5aRH8am7bmaW8/S/4lu8D/E\ndVIa\r\n=Jkmv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGqDIn2g3wpPs/UAnWGSnUrgZxDpuVnPLJUqNQ1P5GtbAiEAlw8w/BTsmCN9ZivFihPMkEvTuiVVe4cJa+yKazMukdU="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjnbgxhp.0_1610052156549_0.3307966220710392"},"_hasShrinkwrap":false},"1.0.0-canary.kjnbl337.0":{"name":"plumier","version":"1.0.0-canary.kjnbl337.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjnbl337.0+59d1669","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"59d1669d9349243ca9d5d014b102c2f3b1dfbeae","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjnbl337.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-qQfjNu4EUGo0RXD1Hn3cpWXQJ3if+Hihbu9nFAvN0bttaLSX/lUEK4otk1jlW29s3l1lCskk3lo2uzxUliPHTg==","shasum":"8b957d5b56d7238bfa371c81cc2297d6314daf99","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjnbl337.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf93L7CRA9TVsSAnZWagAAoYcQAIjK6y95Geux1Wry4XuF\nDJ6fZ3qcctylnMNyLxNgrE7ezYytYjDL7LLMkkcb/22HFfaCJFfy/xjb8NPB\nLZ1dRMYzMjxshnM2ltWIozBqqdQ7T6cfLX74662PNtJYyCPGuR/26zJrz+9F\nM5+8TCYjWz5JuFtMx54drhJa585+gQxs2OiOjelY0vioi3KxqI6ee62KWrQE\nWmP2pefQQlz73HG+yCATRB0HDOzV22aoRVPNyeooAVfMzdoJaifrjlhRGZS7\nPwRgaGJ985Wiuc+PPLycWPju62xUPhTgVy2TfyteV4FUoT1adhgt5HPiRh7o\nDkFAjgiral3Nt82Z4+KwlSuVNGLJNOLnRHqCgkIqEqRD2h+V9p+qhwcR68Hq\nrCKD7i1wVOxR753l8ApPB2QNssK0SCicbPzuNgihFr4Y+dnrnssk3sn9bVlO\n592Yh7IU7sva6MQMnyzOzhLH3xHIURS3Sj32n+oOJ04cJrKtIDSlbyO+YN3g\nj8t8uf1TDi30BVA8Fw1bcnuuG0n11JD+jyuDnYyRAFHAO96H0g3KRmPA2zEg\nzZWbtoXqFoNdnpw6LBx9GD56SHZ7HBq0FAVLD5GTJGpIKHX2wXzuRxErTFED\nD8gGz/0IED1VZXtydI5QmzSjCyaLGcwn8U7IKnh2LlWy41Bt6Utna23rJPEw\nmNy4\r\n=mqEM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH06QMGyPfhI0O0iAXchpiGQmcfUOkT4oQXCP3S7m6+LAiAe2bFO+kzDKozjGlOsh8qgUxNWY+1er/f+BjngHWiErQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjnbl337.0_1610052347088_0.4795759910998698"},"_hasShrinkwrap":false},"1.0.0-canary.kjnmrhgp.0":{"name":"plumier","version":"1.0.0-canary.kjnmrhgp.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjnmrhgp.0+882abb4","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"882abb4ee9f0abe20dcadeb34dce1713e680bb9b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjnmrhgp.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-CdcUyiagVWdgOgRGzGEuCadLhjy69xmtMzl+rj70s+YSUoZWPWCft3ZvYzZ1C1X131NrCjTCOUQQGv/Il03XWQ==","shasum":"dc06cae47f9819511edf63b40e11a29399087bb0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjnmrhgp.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf97xVCRA9TVsSAnZWagAAKrUP/Rr3K3VqJNpfE04zJoVL\nDZk49k3o/97nvQpB8/TImuv/YKMKeFzysr+llLxNfHbIiiO9Nnh/9Irrpy6k\nXGRx/nmSrRKbRy7mAb5/nEKxiot3yevZHfj4Ia84nOvE+P2GlvArNcIhjenO\nrwS3Mw/DGVEBGnEVZCfR6BLVluIFOGMazv0BsA7zwG5BKRwL/FZzCVTLH7NK\nNXLALJNqk0IF/GmnoUO59ovd/xvHmhOYL9rAyDQePXsFEnmDtReb7/1POqoY\nfZ8bfGHsnPR5Wx/RbZ9a4nEh8vONwypIUy+TvoHIGJ5dDOKLhSbhL+RysvtV\nbHZfL8Sakl5Tjz8lSjYNuvAJ6Gci0968sysJOjw8zd9snNZw8oXbijI8sZPR\n4OKIdGk7/m9K8oJDtTZb6K7VyXyZtQar7abwPe0Y628wUVf3xrvSCn7Ozu5D\naS5qYBTbOMpr4D7anAKP2cDE1PF8jbm0VluYhBXY8kpvMewu/WvAn2imGDE0\nzJ4Z18YC0pylXOVIFygly79Vdp7x7IR8QRCIVxQDHiirGV40KhJ9tb+Qpryj\n2SgkppMwzn36icrwtsGZpj+4Vww/NaZB2aEMVFFZGmvkrZa7LPAdnWkCrtPA\nG9GpYKWW9hJQLryvp/vS7B0tysHnlFST0CjcPMEvZ45BhdwbeiGNIuL2t9rx\nGj1x\r\n=vNK5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwwsmQMkYJnIjsKLcElSD2TjnRnNebD2VAybn7u6XqMQIhAIo0LIogjP/jNUCm9DRHD/zE93fSkznUYfQIb2GjbPgS"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjnmrhgp.0_1610071125331_0.9550664402868938"},"_hasShrinkwrap":false},"1.0.0-canary.kjvyett6.0":{"name":"plumier","version":"1.0.0-canary.kjvyett6.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjvyett6.0+d598502","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d598502e8f45514b8af9d9bb820d96dc875e70a6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjvyett6.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-U9ThLVIl3JtmEFWIT7+6phgcnuWoqGmLgUQA9xxcva/IyLme52GlZ9O4mUODuSpkQnxRQE9bSBCF3ycxLb8bpA==","shasum":"e4234353d1fdbaf39ab21680a88ff0b68d3ebf75","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjvyett6.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf/2pRCRA9TVsSAnZWagAAj7MP/3qLFBqHp4emB7h1Tnyc\nYaXzEAAs5mUBRAbRZZAYxaFc3QaNoyN1a8A9P3B4tbTQnBR44CKLw+8/GQD4\nIoY1BUT724/D7rdMwkR90fPWkfHpSC8YKCe1h/Oo44dUXITKJ6zM5/lrASo9\nCxEtXWc7oo5SpUi4IaJP/I2IUJmyBmBCX6T5XKq/pjvFL4Xp0naM/h3VkJGZ\nFoLxIP0o2WMqAIhL2PkPtZv6c0bDkPGNsDuB+xxMLchFjl9Fv3k4fcDFUPm/\nzFs3hGYcUmX+WN3Bw3W9webvSHFjbNDa6Uj6NgQRfSzGJkXUXFydgIGpfcmW\n4w5wykfhTSOA/ZrG/deHp59l86ovsIeZJuNc7rb9XS8g+cLNht7rFt4T+HjB\nmiW1muohzw+9FD5kPXznaBbfa+6hz6UiX2P6gO4eqVcKD8Yt+PhYUQrzwt5I\nvd0lpIPe9ZCM48/laQIqxVSicLulO9We/2uCjftQs8Dt94BgumuC6uh5DI8M\nCbfhokgpGIRZrk7fqMBxt5qOldkduGBdpcO+pH4i1M0/7T+Qfmy9W+g1/gls\neZikYjJCzZiO2Hd6UFNTW0mAGIZYcQ9NjBmUUnxdAe94SA8wdmr3KfQh0lxK\nCaMJS+HlkNsNx5FtIwQWhtzp2qvgmVd2dM1Z+0QPjxo1eMc0lIKWYwVW8IUf\not0s\r\n=emsw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBD844X4whkRKd+11CRqkX8I/cv6eyoWMaqkdERVZdeCAiEAtDd4ixvWQetA7yRMsXgqxEXJSL21EyAu+ZMfdMQA6rw="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjvyett6.0_1610574416585_0.5095465758049016"},"_hasShrinkwrap":false},"1.0.0-canary.kjxsazqq.0":{"name":"plumier","version":"1.0.0-canary.kjxsazqq.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kjxsazqq.0+2981e6d","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.4"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2981e6dfe6e77f699b5ec6f239a43beb8aa36bf3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kjxsazqq.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-arYEDjxYNFS6pUr2gTq4yPJ4SuGdroSsMRCTj2U491TirAk9seqJ+hiPu+KUG/AFxtm4pM8u0TBtU5W6ZhuocQ==","shasum":"ef09c8c473dda4c72691948fbb8de3409338a30d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kjxsazqq.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgARqkCRA9TVsSAnZWagAA3PgP/jSBoCermfmwt4ok95iK\nE2D0mX1XslQZNfnjvhXrfy7Pt3Soxo4Su8TrSEhg1P+xHOqQKviiRbNd32Wl\nFfcVGUscl101wZxBKpqbiKnpvPMtXff41jr5CnF29VQIB968fvL+DKbYYfNv\nJRwEvNMrYTL3zjdH5v/GVko7THfqjBpO6sixpvz7zVnPEzCieISRUIPMEM58\nwqEbw461WdPz1gX1HkqZnu1tqVjlgEbmhHNgbvq+1JJQz5mtHoMhsvcJt4v9\nHw4XGiFHAcVAyuMrL1m+lqPu1LOIqxjNyY0NMkoDzhvYz/o1/BQXpMRlzcje\nU1LApTykkk6WTfhAUUVh0NgDIDCnFK2wO4NgGl/p1L5o41o4j94qhhXVsACC\n/vpKlXqUDLz3Xgjjzm9qMc3iUhAIqelGnzAMMO+xUElyCuSNYhCdz38CUcWX\nParDJKfmY9YpsLv/DwXAc0Umh6iFcBDeG7/Esd1ngbg9QuMidRFgVtEsHsjb\npgQ9PRutvvC122aSzfoGVv7p6MuxnrRw/guGed6fwvzcMggCJjYmcMrfgpYV\nccyswzZFXwNYCcp7Pm9RFA/cUr6QguhNM0oUtzIyHMgVPEEvDcaqCOBIbgJ6\npYuYKK/mljPVr5t4YsI5kGqWaRu/Wk1dhg0O4Mk+2rWtASVj2dEgsRpEeHFP\nWW8B\r\n=DjKf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEmg0IFo5+UkNEBQy8ddDAf8J4oUgIxxua0Og9HhSauCAiEAqVFW64Hxmo2p2UT6lJtwQlBxG89FBUgx8n8vIe4E690="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kjxsazqq.0_1610685091384_0.6375594539726848"},"_hasShrinkwrap":false},"1.0.0-canary.kk11hnon.0":{"name":"plumier","version":"1.0.0-canary.kk11hnon.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kk11hnon.0+e57db84","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e57db84a3c917f7fcd2f8fe84b84d3d46bd90fff","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kk11hnon.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-TNyNFJOfEV1lUSJz9zwRFNL+Zd7u9Pu5DqwGrBWdH8AxIaOIFNZaHrYhEUpvZW9QldzwPdhSpMMwllEQIYKO1w==","shasum":"ff33e7d0913ca6330a8d965710b555c0f63802e4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kk11hnon.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgBBt+CRA9TVsSAnZWagAAxZAP/3CxCSQFpJEprspuoD6t\noYwD3WICci0/eyU6/kGBOvzFSvRB4yoAF/ntGMMghtm78mXAj1H5HtCf/e04\nYTrJ+y9XfdPFOU3dmPQHhV6S3mjhvQBKPDJU3bbClCVtkzdg4I9Pv6W8Kw0R\nABQcSdgjCxxnesIppAo9w+t6+EZ9sarhpyqluH4ef8KIhdGkNFfcH7xsJwYj\n6mwLBRcIn0pdWxAZ4/qeiW3HV6cQI14l3UcMyFaGucweJhAgyxEHxOQzaZOp\nRUchntbojjhBfUlscekeohNCgw//Zx8DfuXqYiFU6KTLYCpQR6vmSMaSgEY5\nY6WIxclx5+JbbkvLhKo79aJwAJc1TUYW2ElbOob9u4Zl2jfNiku2bNU1G667\nZwoL3egEPP+UEWhhOlYlWyJmMnWgx2EXzNrwg2TjYi3x0gtvdgt7tJyCq81B\npAfWx6Xr41d+3rSV/PgTxOZa4ZHbJl+/eNH6Ic3HzK0z6VU/UJZbzvVlIkv1\n24Z74y0pb9INFdufInU74LEnXf/83Y7tAGtaEQS5LfJFwMfXUNoSZaMbgaKQ\nrbIIPQKfGZuXz3vnpfYCP5XomoS6RLjd7gYhEQvrRrR03Xlgs0kCpruUPMRH\nRfL41jnB8ZfxQR0lOTxgfYAGW7esDIF3C8QCu4XJ/7jkw3r6Q1v4QcT75Pzx\nxScJ\r\n=WtHM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFhvtl0SwgPmLm1TYLgBBCUVVcYfCkVmRb8eZq8u99FZAiEAhRYo7Qzacx+Gwb/7h1y5p9h5/mGqYzufSG0cWSp+wJM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kk11hnon.0_1610881918095_0.5001914801519454"},"_hasShrinkwrap":false},"1.0.0-canary.kk38o98m.0":{"name":"plumier","version":"1.0.0-canary.kk38o98m.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kk38o98m.0+975e937","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"975e937a1e7517970d847397ff1ea12deba085a1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kk38o98m.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-Ak8/XKaS9i2lklCtykOv7INvJ4wCqMpwbk12dsw6wHSz0gVT6jPrHbStoXTYtl6IMD6AOzEJssJhFAK3km6g6A==","shasum":"5a55182bef1552eb7ea95403c2a1561cc1c91f47","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kk38o98m.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgBiMGCRA9TVsSAnZWagAAR+MP/RO6nKpG0/ffMANbiN1H\nQ9KQxz9Dg6jy0GLvclOn8uuScGAWybke0b+aUulQcmwFrnBBoj4YHG52ZNuw\nkvBAIGi/zESqRKKECzb4QUsBo3uMz2r1yCcifZeTrZzdSUmhoH/PtDvDpkEY\no4dfA06xH28XBy9gCFccqN9mGQK1gMh4qyoh376hg0plQEoiwe4pNbHx6bHR\na+WZOWS9KvJirVCnO5eivsd3Nv864SoqxB3fZ6LVHn0gQqT/+eT7BvFA5YJn\nDUfGRuXtDOfibOOw1UPqCEnA9xWmoY6dt31Z5hG2tGstZuTB8UHl0MvDJNr9\njpdIhcbWqOPtGV111in6LBpQZhSp0nUAo2ioF2/s9maFsLcT1kkviRfdvl1f\nK47GwBGGEJ+p1TAisCiGLuR3i0zMOyx4LA8ZMWHX2mpFYcgyr/O22uW8q1Ia\nSd4CxyUT8QQnSKsxCgSXWgGHECVkjdFWxKKOQEStUKdwYQDjD93U4xIdRzjq\nfmNUZTRXBbQqYs8mJDZDqUR0qH0bTfjHCsk7xsfJ84JeeJhDaMyAqmqnqY7f\naE+p+NV6w/rWRp1Uh5xTuUuqAGuBcFDLI0R0VnksO6ZP9zX7gOLuqnE+sZvS\nKkeFLpZ2KpekeFx8xDkmZoUlB0wbpX8OdUBsvBttlGysv2AUcMuIQqxl+SEO\nfsaH\r\n=IZ4S\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFWikTTkX5OumxXL3+/cwAeOvLht+DMtSQx699cHm0jiAiB3uDXImD3eah+7pEW3QqUQyFZu5IXStEo7qk4wMzEPrA=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kk38o98m.0_1611014917830_0.9760538080677934"},"_hasShrinkwrap":false},"1.0.0-rc.5":{"name":"plumier","version":"1.0.0-rc.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.5","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6d9b3569b19b7f12bb40eaab444f6ca91e312848","_id":"plumier@1.0.0-rc.5","_nodeVersion":"10.23.1","_npmVersion":"lerna/3.22.1/node@v10.23.1+x64 (darwin)","dist":{"integrity":"sha512-LNZfhfEU6/s+8qVKH8P8zPpU6g41Dn0GJsHHHhHqpH7efYBmaSMgVi841N1j31YtmqgB1uWYNY0fFZAoUbYytw==","shasum":"b119d5e2a734e30f16397a8a0bc1245b13e4c012","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.5.tgz","fileCount":13,"unpackedSize":34094,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDKhrCRA9TVsSAnZWagAAsh4P/RxlT/JVzyiFPIM2HdJz\nQPHyzeugO6y33OuW49NEPDIeKikeUurHZzYpklxDc2uNvwRlDfURchtTOWdj\nv9QKNI5dzZG/9yWJq1F9l8AQVsnuXZNizdYMGEOU9ocs45+prjximXsntkWu\n4XebUyb4MVBAi/PTkYxRCjrnDOkD1/QrGsg+b7hUt8CWRnIwR7Y4n5Ji8Qpq\ntpC00PE+ba1IlPUVxP1Io/4q5dhpFGEcUcSeAygcyvLd0VuoqOEMlOfFJRjf\nWWZ08sI73Ies9csU2cuT/QYcCTTlLqjImu25oYvrAHk8ClH3W8auFeNtCkc+\nIQyMuN259iLUrWM9+L7zb0mIjwvGgrke4BFDNsZlOmceubplKpO0lyk1KUB0\nMMiILTgI6jy0n6gdatAvgdntMo+g5UMGxzYwZDqtcgo6dgWtjiFUPfcoIf7X\nvyVZjUsF9Q6vDu8xfQciiiybPE8X0FlhaW3lbV6b8L6snOWkor7cHaEsdaYC\nlVtYRsw4CmzubVuqGvy1+Rm6DkawVISYhY0bTvdQ2awjaz06i4FUTxgWsoLb\nYZLFlXdRPf+uq7WEGgzdRTseZxFLGq57u3+wXyCcqHXdv3Xqz4JUX2x62jH7\nNkANd7eunpMiVslnzTUCiAWAnlp+0IcP6ldlrImkA6g1B4cL24jPhR6QNnbO\nGfe5\r\n=+2xU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDtDr3REHlJl4QoHoObrbpiAH7JUZ1Y8BLoFjd2AtUSLQIhAMgzOf1eYYmWLXmr/u2jamtKClYSS8iGLeVmaSnsUMde"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.5_1611442283126_0.3222702776265114"},"_hasShrinkwrap":false},"1.0.0-canary.kkab9bkf.0":{"name":"plumier","version":"1.0.0-canary.kkab9bkf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkab9bkf.0+6d9b356","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6d9b3569b19b7f12bb40eaab444f6ca91e312848","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkab9bkf.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-++yZfiG3hViTgkU6A0Z0MMTGddZ25zcxl1ElNLa0m6LKnNnMs6jauzkS5/Ja94Fa8PeNfxrfFou2YgIcMA5VnQ==","shasum":"13f7fd2be4d2b0efd8602088741b4d1ae9ef5868","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkab9bkf.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDKlYCRA9TVsSAnZWagAA1iUP/RUFcWcXZ3KsT8ktwvUL\nd4G+l4vdH2gErDTwnkOPAI+wCuDGCxer5zl5l5rwbt/SR5/6HVGDQM9HRgHB\nUj6rzhqrlwKK5Fnfd1fRSCMLl3JvIDtjZFx3vfXw85iFVcHF8hxtg/OGJw4H\npgNQXvDVttXXMtt7v0qZiZTI8PUAJMZ6WVKA9ttngxBrqAQN0gq3zNLEqyY6\npMNqJuaOU9UlTfmQ/9kOQP4UWqSxodQcU21xHPXJjsW7vAWoBVReYlkd0G58\nvY51Iu8rB2chmuWQdgFCRPUnCo/c25Z3rRHzENkI89f+sXuGRXFEcL04P1tf\ngCEDPtyUfMYXrw6wxE4SdOFJk/BaINecZZjTqiiZygnzkk9DsQtZVqgPS/JT\nm/fz5ngSO6042LP1i4dGIoWe7TUSCaeC8Zcb3gXYVfZRQfU/wxZhGL+SReJh\nSaUTObWinJkBaq01wjGBw9kqK+gabZ10Jj9FIjHTq4Ivimf2Oque0PJYgnfJ\npRVD68YXEHxQrgy578p0ivMMtDb+QJGD2LDo0G3HbvbXeUUp4BgfS0opG0UP\nt0Ptkrm3O2OWYS2XVtHalk+95p8bFOZsc5e0Xb7Af8WnGprD7GHOUTB0uG9K\nMGdW1wSHgmgtfOp8CPGf7QocLIBmIBfE5HTZjStw3demE/uAC8wOwqDdPRAm\nSnOZ\r\n=4fKv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCW9QHql6M6Ier6CDEReg7b0xCt7GodidK3L4lIT3AZaAIhALVoNAHg6cYOtkLwQRR/tV7UNEsOdqN9IpNgEY+ThcZJ"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkab9bkf.0_1611442519593_0.7301634953796117"},"_hasShrinkwrap":false},"1.0.0-canary.kkadsgg7.0":{"name":"plumier","version":"1.0.0-canary.kkadsgg7.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkadsgg7.0+784ce8d","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"784ce8dc1b019db84840cd8361e2bb270e94cdbe","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkadsgg7.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-TLCLSAO00edmFhG+ppD42r9kkoXoUJxEs7vrrrxOD5z4uWSOmjEaD4Z66B01tMidGSMdpRjcDDqTToFiqpy9qQ==","shasum":"74628a165b5db4621c8699f309c1c3ebca6303f3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkadsgg7.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDLn0CRA9TVsSAnZWagAAXe0P/1ldCqM+UYaJXw0jFcb8\nrCIrKPFZVFJIYAZaHsx/nwKH6cCsoRtGW7CyOOD9g/yeEyfhP7HORwy1knzk\nAGzqPS253PmuSXU7txS4ByNDurxz8Y6YRwY+yHwIdO4uk7PUfTWuA57PGRtZ\nTwz2aBORQ7BYazAw2/SMol21gizRw9z7iSKpr4A8YZvQkqdVDNbPHmWTF+qA\n605k575ccRhVzVX/A/g3JxFHuzhdXoPGdatghNBqvRm3RnRbDf2/5pRtnOzv\nek14EiuC0q8UkDVpXUirSgx2qPqWORqjxxE6pI3OjzXhK55K12aR+lozi2WT\nYnzpDtKm5IInLq1iiRipcX4ol3uFnVy+sUfBa0pzWsw7Il24n+ML/PtW0g5t\ncnvYYe2KEAgdxPoIJfaI6b5RqG7J66Nyop73hg7B44QzkbZc0CVmPlfArBZR\nGEMsrS2pqbzD2FnFWxgQaNvfJmKjOzjG7KaMfIFWJurjlIBLRWCzXzs/O6Uc\n6YtuP3mkGaRk6Ihq7O9T9PV37QvBqwxaziTTB5hWlpW0zUpRv5JFUg22DzQq\n8OPGHzW9Iy7sDRjATI97oybWQa29I1aPlZD4kQTOq5GR62ycbzd20M9Ay4Bo\n1XgBdnsPtH6ombk15OKPnI5bGclxfv/gtXJkUg4de342w/zHSB023nZo/9qB\np6PL\r\n=DDwQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDaHz+H7QbwNij3UYj1FKhQH2cFhVpYIKcYXYjJIyfx0QIhAIzbvTQz36llM4HcRw8y4n3oS94zg5ILRUHFYceiFohj"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkadsgg7.0_1611446772411_0.5080576079937589"},"_hasShrinkwrap":false},"1.0.0-canary.kkc8txq0.0":{"name":"plumier","version":"1.0.0-canary.kkc8txq0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkc8txq0.0+d9ce8c1","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d9ce8c16be83eb87f3aa671168daa4915878a107","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkc8txq0.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-aps2d+16A+eHy1OaqWq4Z3p5QoTSyGI1TVpIK7sZI/wKlQWHxYMb0NqLoAV5SPdqgRGxlMbN6T9H09y9VlLolA==","shasum":"7b4d078f1816c02fcfb97c32536611351cf7fe5f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkc8txq0.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDnHPCRA9TVsSAnZWagAAo4IP/iGLf1CuK5yLrDqFOGYY\nIdrsygYcoTn8mfC4acEM5Ilapxgp5ifOMWUKCjpsK0a6VGHE8wb69seLgIDC\nHQamG11of/OWVrY1186+NccORzTzznqNKmt3gEXrk9wTOYyZ+wUaTyXucKVe\njEHJivCC2E7iY46h7lAi6S61lCJv9L/OMfrly8o5CD31YU1dmCn655qRUsMn\nL4xQA4i4QQ9+c6jd98ykCkpqYx8c9I7cprmRT7mGp5YA5T5of+beO+s2htu7\nLSsppO2ZrO6qSgJEPLIet5EhdmLqNvFOzNZ2QYHIFIRJB8JNBfDN2Rm4oBpS\nI9oBL+0lVKlDkbcdwqC3jAX9B11U85IHvH6MOcMjh1wDfQ+OwoR0gPNzeE3x\nDqQ2+I/0YYJIGC6ujFCSEbTb+JBlr5H90zopOAzK16uamqgXP5QzuxrarhlL\n7U3sh0Q2Ul5rtCrdEWaWgmUmsdI61m4zWsp+xCOeW02HEQEQ+IZM2N3LQliV\nWh4e5Wr8HEKYj22KmQE1ooRVHgrmG4ZQgjuY+ILlqVIjlMyd1jErvJL+DyQy\nGo7ZbJkjv+iVSEvVlhdInEs/THT3DAC0ZeF42aQm9MGhp8Vw6EzQLBO0yiaZ\nhqTVTvra0C2gKFIULXl5AM7gxATct4wVtBj0kDcOaAZ4KXrrzcjDj+RCbyCP\nFvxB\r\n=dVeS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAzANi1HyMBzzwczRg8wYzNxagxScDVvW4zA+OCh+RamAiEAuhfV6485c7wSzTPJL8YHMvOZy88cWHB28OwGTUcIKKg="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkc8txq0.0_1611559374758_0.2765514125645574"},"_hasShrinkwrap":false},"1.0.0-canary.kkdoevg4.0":{"name":"plumier","version":"1.0.0-canary.kkdoevg4.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkdoevg4.0+6997bd1","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6997bd1820550bc4eb1012e9e163c0d2a94acc54","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkdoevg4.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-r9WvpK52ogvLg/Nv0IsdfZAsemTBiLXu70Wdyl4J4qGcaZ4q76HiAZ1uBNFD+2jAUKG1YiGKPiKKVEpTTS8PcA==","shasum":"6fe4713d74c3d1fc5067e0fd89a81e13bbace872","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkdoevg4.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgD8RGCRA9TVsSAnZWagAAVlYQAI3tCPpbeJbfllKS4+lm\n0gcAZdTTr+gBWkjIwfpQXiv4NaNqbmdgvdRL0SiEsTUu5UW/NAoHHO0f7IPp\nxsRtph7+ojEdZDJM+xi12Tc+Ff4fsww5igMBDoReViLNeRfy9xFAPN9xgJKU\nHqTQhmcD9DZX1AXxMEvMdxGXMTcooHg1EzsAcKuwESkzoYQT2+CiOZdEb2tY\nFGXHZBE223hkZLhkHw8gT7awJoue8NPrBRfopIj5IySWT/ryHvfqpqDXdxHA\nFRxkHH80/p2h5esG8ckmw4iUkoF6IJYobo9vVWt0y/0XvyYzUybrFUa3B0Bk\nIr/6ykzpcn/2axd3ms/xwwrNB79mdNjWCpIoU6z8jThjLDwgXEjJmlVNV4ef\nGHAb+MUqYMENgCV5rzBozaDEXHEwBq0f9cahKGjBZgasjSpNR9dh6Z8P+t7H\nyL7a+xOdgerSbCKoJHCsE+QmgTToimK57mRGNurJjbTHI33Q/s8hkaIRAG8O\noG4/lWKOQr2EHLRu97Ds3EIvBZ9SIKdF1pKJ1preiMBrkzJxFdn042jSYPp+\n66gZPeaBeqBSKcTQr9LeypdIFpCptS6dOm7AYEayz/VuHYDARIGzs4R1gmqd\nficT7Nw5CvsjY9II4gMJmWC0225g4Jot8v+QP0i34hFKAl4T44fIttn9UA2S\nCULi\r\n=e+Sn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDOB9co8ITNdHrcMoTaZesKn0wm6gD9lWHcSqBhFEM/SgIgIQsq+gQ0zlM61Y1b5043hjPViFBH9F93hbNBHTLDsFM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkdoevg4.0_1611646022261_0.3282229377825372"},"_hasShrinkwrap":false},"1.0.0-canary.kkdoitvt.0":{"name":"plumier","version":"1.0.0-canary.kkdoitvt.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkdoitvt.0+03fd7f6","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"03fd7f617b4b24b1223250aa6e76692ee5ccb1a7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkdoitvt.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-r9oE5vTdL3xZzn7j0uhb2wwRaTEO/Z5xvKN2OlkJr20j02q9PI2TWYjKVRUHT+Zgj3AE7x67olXh/xtGpZaH+A==","shasum":"2bec1f16584a2f58131ea68432d3df3e847e9beb","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkdoitvt.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgD8T1CRA9TVsSAnZWagAAcl0P/RhAtG7NhNlyIpdFV+K0\nQmmDL2ecVH5bjB5d+FkSnCST4gXurohAz5c17V1Iu/SqFYUWHWVs3b0xp2p4\nhewDXyrBNwMVZcf2S4mXhe9yVvlQ7o86deycyOeKlQtt7NjQKAjeUoaO+Yu3\nJDSDBy4AQrXZYvgFJYqxD6BJnq/cAPgKa2b9yIILhU+KNxqFM/Si8uE9lcmN\nZYRQzTxkw+7dmQpxAL7GantnjMa2WKmLIs2NNCdPfnGdY+UrRy5sdgN8il+Z\ny+qImUE4wYVGSyDPlXk+sW7ugFqArf6W/SJxXMgz8xFOYsxUhZ5LKd8N8YKE\nhK+a5B18+NDfhpRNT5s6YaJYO+lFVhTsJ9d7Y9y775jd+En/8G7s9EJMRUy4\nRz91RZhcQEaccARQus/lUK2dTooPvUeKBHNZvUNqvv2Atk4BRM73kF+vVays\nnRoaHDUzPTu07ZIeusGr5uF4/9CpiSdfKbkukqXlaWkusZhHNivzXAyLDQbm\nMd9nl8SbBAmMEVvkljsFECt74fMqVaMJ22UY1VNoW1TXeJO6gf5SastJqBNK\nZFpdAQkwEzWEEasn273QnitcD9WR28wUQYjJVY6+8A4Ts18ydj+f6X6eeAY4\nG4DekXXsrZWgTz8UZ20zSg/yNwDVSudPhbyNGmKKVn7XFBvK8q+j6SIjLmcI\ntohn\r\n=jr5p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAt6EYdPWnlA/qHsVvLNycBS0bOY0Q4X+wlEsqoyFUikAiBguRtualoSx9LUQgIupH7O5/YlCvmK25xHvHmZ/CTZPg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkdoitvt.0_1611646197493_0.7920984660038999"},"_hasShrinkwrap":false},"1.0.0-canary.kkdt09l2.0":{"name":"plumier","version":"1.0.0-canary.kkdt09l2.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkdt09l2.0+c3e3d2d","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c3e3d2daadcbfd6c1243b04da4e56e50fa506e7d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkdt09l2.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-NvCnED/FyhvuJpg/iCIt/stAWCWRwkfRac9htH1IUvVl7EEiBXWpBVLSHUSX95ffRlt9/aq92O0lHSrI1rebcQ==","shasum":"b86dd27f892f171ec01aee1bde5709b9e7477b2b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkdt09l2.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgD+JvCRA9TVsSAnZWagAAIpYP/j9Kc+CroVYbvHr6J3PN\nIxuWM8bqjbu3cbgEmVIdd4sIzRjOaxBNRZANM8ZrpShr5jZjiTjjvqbNNitw\nvK4Pln2fApUGTCRYQJZwG0cyKBeu7udxZQPUkKUXn825NInWRDjvRCtTpC5j\niMMCMmSsw9kR1zqgizRYn66hYHVUS4vaDEWNpJrlIzkkc+cs4lYh/iG3+Cyu\n3kwU/qz8RyRQnKCeupIjUVvdjkSVACFcqCRdsgavcGMc2zd4PifcDA2Kjkgd\nejiGO+5nBtZrRSwcKRIkHLvyKu9hVBbu3oXmspFSYOBL8nFnYPnGY97cyk9a\nWV8zVa2cIGDHfD/ciBkY/1/xCyXzmQc8cOUyGqo++SAsWvrtDIGXMH637kZ6\nu/c+BFR72+zP3ZiUu43tTpOVdSd5d/7riFxhR1pIpEfHl7OShKKR9t87ySGc\nvqzyHaLOve9PcbLv6wSvJ88Lt5P5QLdzPRO4ebNjWfvY1Yixy9f4fk3JD4Ei\nSnlM0Bx/acg2cys6dut8cV6V1/hOePBe9Gvxc3XyNiebebEYbWOEP4VjrKgn\npJMny+Hob9kAm7/c7+r4oNMCQfzBSrwFVwGUJcK6AHtrhAEfExDVTMC7pIyr\nlVUCgwNGyzt/k1fzfT0Ncs6m2yCOV7C5KLJTbg01d+dLbF7GxFJ5PH2/Urv6\nH+2p\r\n=Idv9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDQdl+kN968wp0JgbWJo+Y7qHJaYFAOtKfIvcZG68u0aQIgJnTOkbVlMMGWAl50YBzb5A5SrPqWDw61DmXjxlmgPu8="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkdt09l2.0_1611653743411_0.5497965896196069"},"_hasShrinkwrap":false},"1.0.0-canary.kkdt2hvw.0":{"name":"plumier","version":"1.0.0-canary.kkdt2hvw.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkdt2hvw.0+491c68b","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"491c68b46b738027bf66c484b3c99130ad84bad7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkdt2hvw.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-86X/yjtyhTcynIvLyi1Ineyn+w1LSHU07rNBcSP2hAL/Wykw79PPbkYmMcYf0HAADOCMbK37qrq4J+Hx2x+7lQ==","shasum":"0419f878bea00c7703c781a4c49b86dbbe314b4a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkdt2hvw.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgD+LmCRA9TVsSAnZWagAAIkAP/iRmVpCxdAKtmtZGvRzT\n0+nVNJNzCT1WjzrUjBWdGNqU2cZer9ntnsplUfXhkZF8m8IQ3ru/dgrr07lv\nk3izzbtvJBYnOxl4X8ilS5QYVlxKRzGQYQPnfY1/yFPCz5U1tsetqpSQa0Cp\nSXyvKRcrob1g9RHsfH8Bz12v8Vospubz3fXiVacJJUvcAV1R9tA/+hX6SJnm\nEbX71t+PVy1WqFsuU0C6lRKqDCcGJwXUSixrPoYQbduBY9kXdT3gej7LACBq\nHRFm+DxtuUOkqdW4Eyf6O589CSr5ut92klYy0KIZlS4ZZ8PCg3H/r0TFNORK\nXKy0HC3jX/AcW3i5dSHmJ1SUd3EwUG6NQT+LNoBfrGUbrn1ZRoHiyYl0896G\nvFhpmj1/uOn8t+ZQkP5fWF0xbzs9FyPJo6pa0nv7jkTPINZETjFBDw/lAHZ8\naYtixLOsrxYDovnPtt3qNRT9P9NzskM+Q8qIwC/ofixhIvIy9Jcwm+s46Pko\nMygRr0sFExate5XiUS3edsw9Bjn1682g5nepyKSOiaxdmeD0l7y/ZxBWDM80\nNNYqrtavFD7W5A3J/aI0E/iOZD5nE/UEldeAox+PzO+a8W9RMbHwjUd6zmjt\nSlZslIu3OcS0SKSIXbwHerqKo99UBYebAt7+si23PfDPLrLoDAfdvdsMcE+P\nbXXf\r\n=FavK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3RasEJQ3zMK0qr9/IJSjqUdwLTWfh6UdQ/TYcgoR+hAIgEAWSvTStjEs+BBpOkdDflkmVB1Z1X+SZa39+VFPWsYs="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkdt2hvw.0_1611653862035_0.37123604717129655"},"_hasShrinkwrap":false},"1.0.0-canary.kkejdvjl.0":{"name":"plumier","version":"1.0.0-canary.kkejdvjl.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkejdvjl.0+5b443bd","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5b443bd4abc115c551df42b6291d1cb660bb4950","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkejdvjl.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-rhI8FiCGQJv5AGvW1l8Kun565KevjNjz+7PPebLzYMR4AQvwhkHog/2kvzg6ZV/ama4tnNiwWwoV4TXBQkJhSQ==","shasum":"145418fcefda6ecb432cf605ac7933d1a4129bd0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkejdvjl.0.tgz","fileCount":13,"unpackedSize":34136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEI91CRA9TVsSAnZWagAAfugP/1nLA8sHuzv7qEGE1Bb8\nKOblo0B6eHJUMyIkz5NKuxW2qjnL05IZsKlCKyjvIuvPAZGZg2sBmqxmFwJL\neq2GjtCoYGkEvFI4eTJuJBUhlSEDBtgSVV6TNEhejpUrhpe2WnaYDWv8amEQ\nY4xCw7jdks9A4fVcplHfGw67FWFzdEyzmZls5oWiGD/WTIQu+W9dIXg/+qVS\n64EmN3Sd5QYsojoYdVlH4lfq4Df1MRYs575zt36jtrnTyP+/Ekzwz/sH9IIp\nQJ1xizCVBD62QBI4+xsCIsXFPWyb5XJIwybZom1SRB83lhvOOhcGk/sG/tLk\nD9ZOK2Ee/JwmSBLqZwH6WunIIrh21wYDBeubgmynpOkWge+Fha+CaoBcS4XU\nD0yk7GfJVfhes4Xx7qtju0zChaGSp/xny1yh8hj+WhFqZpSerQUZcWQBJ3q5\nEuwr6M+zlQgpbKoz+s3P4Iz3O2E5+9TGPV/ZN2Jekh16LYUc//7Af5MXfGCr\nLeAGD8j8ETFMIwOIxWNWx4gpWT9rY66B7lVg7Khlmon0fp3XQUxNkBoqobZ8\nqUQn1AXs2OXeprkAe1TgS+eLrgd/UXdw2WcmwZCUp05tf09R5zBsvIRDbXfJ\nY3zlEdlJSzcGtrJHAqGv+jgDGdBcbj5LbYxLdNwbL+eKsJTt96Gpt6zYhx5W\nzWGA\r\n=vrTZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB8/Ez5Ek8PQCx7YYdYsQW99ekh/k7ZrmKisxd0I5sFQAiEAzcQqS1va6nrnLlzniyeOLg+kuTZ0DdyQ1wy2TptVRdM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkejdvjl.0_1611698036642_0.011013889934224252"},"_hasShrinkwrap":false},"1.0.0-canary.kkg5b59d.0":{"name":"plumier","version":"1.0.0-canary.kkg5b59d.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkg5b59d.0+27a936a","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"27a936a981812df2d7db52acc9c1c902077c21bc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkg5b59d.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-ZuD+JKfX1e6I4xf4MSqtSCvmRuyeDb76PbWAdTTNKFPZrsSzushsPdZwBbXcNkhEY9BYexnOu54JTVRKGxyuBQ==","shasum":"9b4b2836e2c96dfcffe2808b8416657cda59adcd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkg5b59d.0.tgz","fileCount":13,"unpackedSize":34135,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEguECRA9TVsSAnZWagAA+JcP/RNfprKRw+G6rVWnL2Ft\nQ+nk/1g6F1hxeQO85s2URnBidxpx5H5KPGMy7TbqHfIqv09fUXIHKAbkYv3Q\n4Mp/B4kHLzBIHHJb5AJ9dPEAEQEKCA02oyD7+Zx0RZLS3fRDMVXNZaSJYYD/\noP0IvNPDu9GbHzRnRHUWzWfPgsGQBoxgF1tdBJ1lFOM/7GqA65j6Ewzg7Bth\nLRF4GnXNUNY2jCGA2rcrasnnp85VCo4ud4sn3hJfyWS/+Px3/2S1ntDE3KYh\nkJE5utBuJyWWYAMfYiUTUqx+KO1tYnEIRNKtkRXPuWW6+j2XlP2/gmNPIyCa\nPLFsBJu7f1QQ7/iJeed0gttD0mQdoTtUo2zHqhaOUr/yCAf/093fpecCF9d3\ngVni5ZYmDv18iAmSplloMgjLiIDUID4vIE9SR+0pTkKz5q/KIVQeazuD2nXc\n/2qTm8x1S/y+PI0J48JoAx1deW12tM4s/Jchxn33VZN2wYWtlmMIAy29/YWE\n7AJq4aAyf+e0MjjW21wYzSN9RNpqr99uZnVljzfWnZKLDAy88tl7/ipea5ux\niG/sax6vPQktJ4BvU8vV2e0GwIrKtIoDibbnUBA0WEUSBoN5a3YcxIeJ/H/l\n2GEqrzFncdD9MbNL10smviLjTgJtkaBZNkkgiZs8rUrbm9va3CTyi9Daq+Ou\nceE1\r\n=3kDy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD72UB3EjgeGuHzg/H1VTuqxCNitqn5DU5CS32y2qY4LAIgaLRLMFnrL9UCtQDsrcOBhi6LtqTjFvhscF9wGNLV4Z4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkg5b59d.0_1611795332482_0.7170894177436256"},"_hasShrinkwrap":false},"1.0.0-canary.kkgqkazf.0":{"name":"plumier","version":"1.0.0-canary.kkgqkazf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkgqkazf.0+a8050af","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a8050af6ed4d58c5a69285f3a9e10abeff598858","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkgqkazf.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-Q1iJLLo+vVy0+lY3ijWJj/rONaf8GNczXFZSnW5E+ayFINilAOxOvX21K912O9slwuos/g/5jeqORRs1n0zU4g==","shasum":"a38179fbb90f752eb4cbcfcf653fbb3773ef670c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkgqkazf.0.tgz","fileCount":13,"unpackedSize":34135,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEpb0CRA9TVsSAnZWagAAsA8P/j+xIrv8jVIJa56/OTU0\nwFwUeRmXu6V9SlaHT8mi+h2B1LEE1j9GdDO1Tit5e339sST/p1bdhC9TOZ2h\nmfA/p3fc2zpPT34VVYCzZqT7GXw4KvzSFB3Pu4X5d5JNDFKah+H9rhTvjwbc\nO052zRxgZzWj39n6XBRWVgNacj28Uez8ccm3f2wDafbCbV3kLUp6cH85o+//\nj73TMQCdc7uNmo5nL2dwo3IGdZu7iiH0lmSxW+U+AgGnHzWI7IJWxAT8CO4Q\nEmvJ7euu7oMotKpPatsRKLHb2i6uZIOeH9mXxG3KVjT8YeejPXo/psGVIAgH\nodtVTHJFU4E/OfyuQAfjxNYoI6evv77iNGZZNiO7PNb/w6iW06eh7OtuBEdg\nFvwhU9JK00BEyxZSzL2rn9+sUlbLpNBXQnyLbeET0Ru+FV5rUbtKuUVrxEN2\nFputT1eloO0EpB09nmr7er6cHKRUULtIjgXP8rRVnEqj1IyFBsCYxkRalcPs\nZEjtDc/6hFhW5860w4XTlDe1Jfl5DFyA8g050Qyl7hNa5YT0/21c6KjSZgIv\noPiuY9BZ0/TTIN9U2FCiaAJ0c84N7b6CGmN618uVdGfMLLpD7pMDawSEKXXm\nCuWB8KExrDe/TxoIyaI+klM7YtpX/BIXN10RBeR/NqnkYqbaLKfQeHzm+6a0\n2dcq\r\n=X1hu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZ4wdtG8EvG5BBE67Alu55ItvWnNnQ/W9ZCtwTm3p6IQIgElgQdjV2t1mlNPiVZj7N79d+qMBAkpareh1G2n72JXA="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkgqkazf.0_1611831028337_0.39428934320853926"},"_hasShrinkwrap":false},"1.0.0-canary.kkkat4et.0":{"name":"plumier","version":"1.0.0-canary.kkkat4et.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkkat4et.0+c10dae0","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c10dae0d36172740b031d4fde125d1f8bc06d1fd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkkat4et.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-vzcDKcSVgx7QsfY5Y4hwKo9iDfhJUGpqGKVFyMFIUcY9gHUZMrN993l1gjqUUZR7eKlUxii8FKt3bo5PT0octw==","shasum":"62323d2e5ee2e4a97c7259d4b3a458d121819d08","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkkat4et.0.tgz","fileCount":13,"unpackedSize":34033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgFeBcCRA9TVsSAnZWagAADNcP/Ru5nkBWjeOJxbH31BjT\n7vjTW9Mxdb+4+bFuErSzu6IucnwSMQkjWkoj+HsCFIa4DBf9dcTKdX+3dzVW\nCXmJtFS4DpzPP8Pe/CyHOgohxTStpdC3xpSOKqv446jtNY833D5bzgOYfVDz\nV1Y8FrVFCtMLwl8Vh6mHs2ylvKyp5Ro7FVfTd5a+VZaJsg672yzSxAq0XjDJ\nXsmH9wSF+6D4KBfHXnNX9/ckr/DIP9AtJzxGWIdnLeSo7147AiCyDx3oEqBV\nP4bgz0ddKKw21r9OZhcK/SIO3t2pCSKYUr/yxE73LN8dDXbJbHIqP9VHL/4B\nbb8BPtOfgACt7ahW04hw/PwBAuu8D2EQfeHm17BE1o0dOhCBJVCTxDwjvH+2\nBKqmnbx0B4qM/DgXtKKmx/FFbCLjX+QKazirwAgm/hciEw6SYXTZ6x7j2JI8\nFs8xvhEhb/f8HyOAGm/4XuADG03U7/hgzM7lKNcn6dKWqAmZbeZgD0CU8iYW\nzIC07VQHlzJq/mHHN3pjEndWtDK85gUUXRvFQsutivTEFICO7nSp7XKbiqYs\nk0DbzLTOnrmifX1DtdGYM9DcKgcrNAn/fbPlafajIKKYrF/6j4hYXNCyXrnw\naGqNNGCYD82ihWmA3mM8pSyXoCJxPdJUNspp+KAPZPVyylDUr1kZsleO1WM2\nre4p\r\n=b77b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC339aGzmK44VBhxABr53vvOxewBFeZ4uHt14t5rb2P3QIhAKVDxPN2vJYxqDZWce81TG7Gi9tJJXXTR5YzwrC/G8RC"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkkat4et.0_1612046428003_0.9175236639159825"},"_hasShrinkwrap":false},"1.0.0-canary.kkm3mgeg.0":{"name":"plumier","version":"1.0.0-canary.kkm3mgeg.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkm3mgeg.0+041813e","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"041813e4825c2491fec0acb2ff3638bf3376274a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkm3mgeg.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-lVYFgviUx5oYd4+3sjqLQ3WNGStPoPgyc58OKh0oF8aY20S3dPy0AX3xyhaPLaH+4avuPQCQH8HEAl4wKuYz/g==","shasum":"189b64bc203b8b267f950585fcd95892d45f2560","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkm3mgeg.0.tgz","fileCount":13,"unpackedSize":34033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgF4mcCRA9TVsSAnZWagAAcPEP/jyoWJHGD+36ZFjc5K+m\nCHM8bN48R8tX/5y+dwc/uCy2y/d87sM93y5vUeFwyUL26zlZo9aCXZRq1C/f\n7zQOCSEbfSeNy3rRakkfXKSpTx5pdY4MMeKSAQ0KGoToYl83EPLtAAn8b8D3\njTLou8GoMnGYIIHh0nuVAJUntyh2gD+aQmkitHw65dLbZb8XFECHqQGA+1EM\nuUQwz6SMz3yXETQYCUjLnKOkbpwbEVpM4ZrZZGnzFx1lnYsTauKmkkQFHGCB\nJSbiMtdLu7CxD0h0ZTAbHEbfgsRnOdRTTjYKWSDKHjgEi6K6ek+/BuYcU4wq\n+bt/43+kxQlaX+PAQlNnmXa+tKnFYzTwuJdVo44k09oOFlhXLfsFLM0kyOz5\naxe7Eea+rrZ/RvhrjzIgPVW4bjWuWUlWlPrNKDjsYoNikBdDMZzdCFplqybk\nzkukL4EJXxPAaHfjiP9RAVCzFpKvuXdJR+MUiMInU9obvQlj5MiOU5q2uLzR\n/z6wfIVpRzzegFOz9MYuQJ0I0QOZ5l4c5DjyUfg8syBJI+2aVr1cB+oH1wHm\nDMV9TcX7veOON2R/NoOlJB3EYCPrJS60JtXkUoMoYQyMGrWTBUJKQ2ZhwIrz\nm73hgH2BqaI+l/eUdZWRs/PzDboTWp/Gs2kVgDAaKIGSjm0aOYNMXADvfiDO\njUPG\r\n=b39U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC6hmaSl7CxKKKVLRwOPHgfrKCrhSVfmf0+66fBxNMsmAIhAPkKR/5nUKtmoVzslpED/OWbWDaMDXN845l60Utyrc3q"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkm3mgeg.0_1612155292399_0.1766692182859082"},"_hasShrinkwrap":false},"1.0.0-canary.kkq0pyxn.0":{"name":"plumier","version":"1.0.0-canary.kkq0pyxn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkq0pyxn.0+371ce4b","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"371ce4b23a2097d491d1414045bbd636fd67bd94","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkq0pyxn.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-drLHW4DFZbbu4aKXvv7QKq/fGyYXH6Hft2Cwdrzn7F6jIKQHYTt5stdc9VJpN7CaIh7WD6vQLlwk9oNILBNAaw==","shasum":"32e5dd1b23ccfa3e92fef3a8f2677ef0739c1284","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkq0pyxn.0.tgz","fileCount":13,"unpackedSize":34033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgGydcCRA9TVsSAnZWagAASv4P/jlIpwLhCNDiGiMkw/Fp\nasbgP6lzI7McNsRbhvBFmePgvNXnbr1xitLBuFs02pyJatpmYndgwAKGFF0K\nPLiNtFioV2Sky0YlExVftSWTaftLx1WmMpm1S8bFRR+n9khIwOJHlFnpjXYm\nXtfNwWD2j8d25EN0YLMznUWZWdW3FsJuHSwB0WG7JQV4K6DdWKTylE/s083z\nmZ438om/Mus7EOaEGy1Q7PS1WaM5E5dengo2/264YNv0LacKZgSwyMQm157K\nt8vg7ZoC743lCqG/x+Dmh6n5P6jlTHryYV/py8sflWCZvpBRn6GQrxtnHJ8h\nvNX9z2ETZdiFLicy5OYKSwDfc+oOZ8iqy8aaEEqe/Mmtud58sAuuJAiYTjxo\nKtS8HQYG3uOwRhFjL5thWXoaRUJbyAPlOyjdWJmOTIo0Vos+H+EYv77JtbTn\nEZM6UujmagXXxxoWyCH+HYqIOFrY/AmO7vTd4FLZmhHJs06xbSjLoe6mJh0s\nt3v4BDLRHOTuAdRykPQERIw1n7w5slnfwMXOFRSFSaw5QegzJ+V8l9bS3mY5\nc9Yn7/NlVqDl//vvvGV2dTqjmqtR7PZPlxpzvvJXT5lqrA/mGFH9W7nC00Bn\n3BGPlGPwG2GSyXHKbYC39p+Z9OJCfLORzxU8NDu4zDQNwAedGOzqmJzdQsRM\nK1zg\r\n=ssAC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCeqJzSPK8pdSrDhDP68qHdGdXXy50s4vX905AbPTqMVQIhALF1JUhaeYRlSL23nuEpMXc5d3qz40I/Xrrd0e6drbjN"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkq0pyxn.0_1612392283830_0.37297830633497786"},"_hasShrinkwrap":false},"1.0.0-canary.kkqennf6.0":{"name":"plumier","version":"1.0.0-canary.kkqennf6.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkqennf6.0+b2f4f37","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b2f4f379d3a68215595c1d014a3927b34cff8bd5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkqennf6.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-PY8FK2vMmJMdMTr8G9LWEm9N8dg7AB4cnIvx64G33o19EJrfxaid74ybMUTnr7F+uT+w9ELNFPkzNjJsvANj3w==","shasum":"29587650fcdd9c3e444864166a4183a5768fc47a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkqennf6.0.tgz","fileCount":13,"unpackedSize":34033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgG4LPCRA9TVsSAnZWagAAIJcP/Apo2PvCcuJbLHzXnRgI\nH46lf9JPlE0tCn17me8itUdrBFYsf9GSZL2RU6QFbhr50PfKCC+4hZLHNBVQ\nzeWuDXdnuVntOSLQH0EfW9bZkKaMSfLtZYq0Eq7G/TAq3c2zjqQp6VeBda4z\nvRtlfCHJjQVv4F9iEMX5CBJ2CKzFp07In06vbUgmkDQX+taN3aqS06+9U1BW\n7Rsp9FDQRwonxDZR9IuMOcj/U3ahnoxsr8sEirzNKlbUhYWNZkb4CTxBYwgS\nDXkwky/Z8OLWCeRCJSHrbtDeHEcOWLRDSEaAGwT3j/6RRtrveJZLPIDLwioH\nuveJ1yWrNvsoJmPNaxFEpZPvuS0XHrUKn5B75txj/3Qke8hNtX/xIhS2kYbi\nnetQ7svFEYCxHnOwR8P/EJEDcA8k1I0zXMrZmFN9vlQuYo2TZUqpRriw5BuS\nbZk9BwF0N0uQYHQwgmPGCLHhVY4IXqS3SJeH6iZdhaCjMhs5xIaeod5JlQDw\nt70y5xsWMNW+FgSa90m44JHuU9fTC5EWpxtDdfnop8COGHLZWzsnp3r4UQQ4\nwEvxGohuXY/utvTcXuSloQ6a/0XrqLd3l0c4R4O6elUx6k5DUeZQznGoOHVX\neTDivGx+9qIgar/8jFlohUr4rlP003rVg/T4WVoMXfXf6oOQscT4mLKzLJLM\n/wHk\r\n=0+pg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDgV1j4QKvwFTiLCUPwUqQduLsE0Yao0FbJXhbeVq2fAwIhAJH6Nf2WgYBuWwKPWkYsrjNWsa9fgowAcViBHRhhJ4bV"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkqennf6.0_1612415694777_0.5336102096996995"},"_hasShrinkwrap":false},"1.0.0-canary.kkrk2bux.0":{"name":"plumier","version":"1.0.0-canary.kkrk2bux.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkrk2bux.0+7867825","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7867825e05a3207a5957fe8f3006920fe2ebf8fa","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkrk2bux.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-V/fAqKI5Hgsg5FiHRlbOZaHWxQe23kdtDFqoE+MQNiaUfLPqTWVxuQSGePBQF9QxY+LbhL6pKE265bDGvTQQrA==","shasum":"897fae05f7806b9571dff017e97675a49af7a76f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkrk2bux.0.tgz","fileCount":13,"unpackedSize":34033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgHJJ2CRA9TVsSAnZWagAArLsP/iCRWffTt4pIyRfnW5N6\nqpQwg+uPMimCgo4iqIy7OG/OuCYN8RHVn8MxFc7wlp6cdof/saDtmXoV0AJU\nQJk05qYGn1Lph565j7C8vuMEx549XybsZTd5bW6LvwtoyAQqu4rXSqAcEMgH\ncrxfwLTiAD0TRRr0NN0hYO8Ank5/Vct/14Hjmx/mawOl4lV1/L3HlMhzpiUJ\nG74dZg4E10/VojUJnKSFjwZxr9EnOboKeBsIFSGYWx9OQM6iAsRU9vSI+lnt\nCu8SVuj+gTbIY2scjdkRBmQuRHc7Fc9kUzuNmdirfLRmsGxy9fnMsYM1Y/Ru\nLGOdGSSecZ++5Iht8ccYEa3ox/JG/4sldH+h6516/VyfbD5HXFaLjGaL0z42\nMXTxEtHEO8sQ1JGkk9DvnYyKFmNPf0nV/Lxoewlx+XXnXDnyMWUroxxkbzDz\njREio+a/G6j/J+drionO9kH0JE9gYBGPDc/m0RmHLdP/ZUYbg0DqPqW5ZLl2\neXusSg9CuDanYaGvQq8e7mWSHN2gDGprv7yzhXFQMAiiW4mZZiQvEY9+1xHz\n7ExGTruv7ivAPs9qyYFbadYgPgGSIl9vB9b4fmedcyY9nrcSDtEmW9FJqnYL\nc+gvbjVEVF0L57fY19fDBOwYwZ/5IQzg9IKdaqDAQE008eBZzXhTF0ClGdMf\nqDT2\r\n=qrcS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG0mtPyud7rnXxsu5Jlp6LQw1sdb7zV9LhCMy8fe0PNMAiEA/jyO6xzjVMuLG61Bf4ERCaYf3zNLrJBpGy5jhdTVnGQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkrk2bux.0_1612485238341_0.8535238655726403"},"_hasShrinkwrap":false},"1.0.0-canary.kks1yrhn.0":{"name":"plumier","version":"1.0.0-canary.kks1yrhn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kks1yrhn.0+c0535d0","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c0535d01083505ec560977019f9f5dd1f15f477c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kks1yrhn.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-BrxJSPBeCkRJ0x8knmRbERSjoWMwu3KwyB39yottV0l1rvGd1DOndUh1F6Hcz7GSRnDzc/doeSKFKa56uFnD8A==","shasum":"c9b68e1382df9ae501a6c391f5ec6a51ec594098","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kks1yrhn.0.tgz","fileCount":13,"unpackedSize":34033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgHQfqCRA9TVsSAnZWagAA4iwQAJhkx4z7GFLXxbOOwRyK\naR1cVh/n2mQ3k0MVcdPWRDYuF/ngKW+dM0tg+hYcsZLF/gawcmuK6GtfuecR\neW1jRObBByOXe+ZJk68wiPiDDvbpCZz+AQcJqi+9laj/rrJBv8rrMc1bZzEc\na+8lNGDmX5KGvDkCUPUyF4S3N3f8l3i9e9LtIJdd5+IHt0kdLNOPsQ+8baw/\nAP9jRNisR2J3ze9+qoRIbC9iYQhj5Wg6QUvrI5HeRyAZcux+t9luw6zl3c7N\n3kVaXSHCELh8xzNyMetiu9DZKQI0zsh5h6xwgOI109WUL3H/BUZ2KRdBD3Hc\nN2srsUHu5QRzmfPmmvT+AMzEmQe+PB1J+KRibPbo5cF6PUXyUUpkmxzYDMq+\nExpe8a0tpdyJQCfs0nPnMYz18Vapy+iwLGXNGfeCXOjIYz3QOK5NgxVQsgMf\n8QZVrj7a4mG6mmipILVWnuxaBdZLBuiWwokuMcfutJqw23O1Bk17qM60Eib4\nCu8aKq79Za7Cz8TXhsoXH4XzPmDsEwEDHdQzQHFzKwLn2gegCJY6lmodNxmh\nvGtsTP8ensZHW3ZThm23uXzw+dxZ2gBzKzDDtbeVT6rrLuw3GJBsCNxI7T36\nRsW9DNymSkoban31HIYAGhRegiSop/pp4f6w3BJOkiM+ZsQRf7OnoTNLz+z4\nLNOt\r\n=Fn59\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZh+6/wIvexghDEgxYbTyNybe1kab3iEyBHrt9VFk+JQIhAOgGq0XHR6+zkmnLalaNANpc+EVGJ3q22WYZ7PaLLNrt"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kks1yrhn.0_1612515305809_0.8145733352431836"},"_hasShrinkwrap":false},"1.0.0-canary.kks8erbp.0":{"name":"plumier","version":"1.0.0-canary.kks8erbp.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kks8erbp.0+15da4b3","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"15da4b3592ac3c5e928f0e40e95b66425dbd2919","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kks8erbp.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-ejN8rAsHY0vQazlVxBrqCu4YgX0xYKrkYXsHFAe/w+cqPON6gxH1WJzfo1CUUVprofTvBVJbo4GEABtMCnfPbg==","shasum":"361caffda0e4b3fb7b101150658cb3669e3b2242","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kks8erbp.0.tgz","fileCount":13,"unpackedSize":34033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgHTIvCRA9TVsSAnZWagAAmHwQAIkNq3b2xsXiyoLLPfH4\nWgQunaF1b9RQJQwEY3aBhakmNro9rdu+ZpfGNRqbM0C7E50eFTHlYks7v1qH\n/1kM8ML56gx0TRLCWqjaMJRTKA03WEavQxsPWIjcIvM0+juykhZG06a8Fm+J\nyt91cExQDHwX5dEZAFQjYrnCyspnsXVYgdEgz8vCipSVtFs5Khlgm1PCfBJt\nxpnwQS0eH6M4oGZMBXQyoORwGjX9IHm7u3GzOxBQa+5ocskFRDQewBDvxMav\nFYl2GzVRJRgAiCo7LlGM5WxXKisZAdFdm4NI9S4ivn5b+RfeQFKoJ4OyAiQ5\nAzvRAFn9pIiGSMAppIl5WksgahjXQS0dmpZKapjeCgyQx4nkgOT1U2T3E4kQ\nIxG6msamZzEljQOAqBNMCKFAeKG7ZvL3NbGm+45OOmQpf0uO6TZBKCInEJ4g\nVgFO1DFZeQzz/e/cADZduIKfirJAr5PXf3fBeRTx3OdCueOULSKWG1cO+z56\nuGkjtkCS5cVH79KLQV+djFQGbBIh3QnRgSf7Z7lBdDTUadPB6EtwOKUUhSoM\nvwEU/FBZhk1a8Jl5ggsR4bldVtpxTUVABJcWcsJkqyP2TpYVXKpre2lbegYY\nwRHQFRtSmlVitaAAydDfJi5EHamptYaXJqQ9FgaWpb5oIJIBG6XfQU9+o8RF\nBeT1\r\n=PV8T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG5rPr3Ypkt3oGRotYTb0rJHk2/uOnAOm8qWqdaXPx+sAiEArsjPVWpjcNAwJDvzfJEUv3qirCFjHUYpR2++TaEMGBE="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kks8erbp.0_1612526127352_0.7557544385963744"},"_hasShrinkwrap":false},"1.0.0-canary.kku388np.0":{"name":"plumier","version":"1.0.0-canary.kku388np.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kku388np.0+9bd74d1","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9bd74d15cc6d7a2a1c968de2c08d04df071c3e20","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kku388np.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-saq1OyRLRnN6f9ho1fjT98pKln5rsvaEptGrjGaN+Pnt1n/tINeYgdswiFHkefzK+WgDJsF1n1E7pQNtuRlD4A==","shasum":"ec397599faefdc15c90711b5c6117ad0352921a5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kku388np.0.tgz","fileCount":13,"unpackedSize":34033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgHuiVCRA9TVsSAnZWagAAQaYP/0Z8AiKDQ7e3XzLD0cgE\nPaf3w94XwVtqBBXUaL449s1GC1TYHFZEmaVBGUeo2CV0cLw8cojlzBNFhoj+\n8By57FyT0iy29Mi0xCE9sIIvwdFfbRjl5J/RNPqIgX73p8A3mKiDiw5HUNvm\nh2Tx5Fq3SJd1ghv/IJKlynuuQ9kKescQN6eaw9nCj1QiXEKJZhJaUpg0sggQ\nGK5nZ0OdnhJe/UGa7UmTYMdmPsFCoVttj0OjxitjbR6vAih2x2PZd4/4H/OM\n7rTGTWmzpdcdnRcfpIYeAMRF6YSP15nje/ptBE1Pws6E/v/UaCeepJ+et3OA\nVoBI9k/uBZfpT2QOwhVOCabqvqraQbD0thbr+A+BDF5C/zjOed+2etdUdeUc\nZxcs2YgOuOwCe7xUzB18qpfAEP5spxaYgH+0kyNr73BQqupAjCGuvBw9K/HF\nmgn/z0lmaJsHDDhtzR6xu7ZfqLntxbAP5P7y1CLW6ivhHBrNggwWjU5IxEeB\nrlWNtuVx//T1j3TPXUcRmvmt6zsyqw63czivJMWbLr4KfvqVSTIqW68zngSO\nSrjpGSg6LkZ09JPof3bOcd6Ob8ww+MFAn/cKyXxJCOfTW4vlm1yDIPVDZgYn\nXCMlq5W9R0X22FsSUxTUkbcASyq4YemYJdKfnl3tD+R2NakVCVrM/ljShGEi\nbnFZ\r\n=j013\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHyuz6wyzFp2U7JnIpAqq5nPaKIGNHMvcYYBVrZfO+GyAiEA6aX5Ruu/WtNHyxUsmfyjmAPmdjJ5mXOL4oTGK7VEQGs="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kku388np.0_1612638357430_0.48017838334944973"},"_hasShrinkwrap":false},"1.0.0-canary.kkuufn5p.0":{"name":"plumier","version":"1.0.0-canary.kkuufn5p.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkuufn5p.0+6ea109f","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6ea109f4cab08dbf2c5eb2d1122071952ac8fd48","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkuufn5p.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-KyUlryxC5Rx0Fk8uaE7vkkmi7octAbtuN6pgQl98uRE5XhovnR45O7vKEVtI3+JxiLcOl5mZa2Dm+VvbPi39YA==","shasum":"faec1fc09164f15adc425bf45715e92f77aa36a7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkuufn5p.0.tgz","fileCount":13,"unpackedSize":34300,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgH5scCRA9TVsSAnZWagAALekP/1+r9HYjxcvRwWqcx8h6\nL+0FTFWpLtv2l+5Cf9799iYscm/hNLJZRu7mxRp9I9jOFOiGAns1f1w6vbZS\nZ/LwiF2sHXtqJw3P6jgeHp+U+83F4fjC2yTKsIS0AV0OqwhXmatmGT+6+rEw\nIXrYbmTvh166BmBrbYonjpS3/vNg62fP4+vShQwHa1tFiUdPxS6eekpXIbGv\nN4RDbp/ORCRdLYdL0Jofn7pK3pKxArRHkkwa2adU45xg8ARlqP/Et99iFFlo\n/iZ0y25jh6cRvZCe+uu9edgJlX85xIhiijonfneqhDJSDDwxsXA4v4Eq3/rv\nN/F8EAcFQlCpytLcT1GlbcN0f2praeVOdlxBqksYMF6OkYpkFbwmkJrCT+Zb\nrMojO2H9bHieJSHrCbthEpkQLXKHN1ic3Rc/QJ8nkTxN1inyN218GyuGEd+E\n271w18znJWNssJ19UrxTva1SFm25uSHv6dbp7UYqSgt3Wgek1ZPP6gQgAndn\nBD6Ajpzcyo/2x2ofJjGwx656bDCphTWxbzh9p+aTdF5etnsGOcTRgGy3Zpow\nswVczdMH13n4ebKL6KFkbzYx7SW6Fg606EjkgBAvRdgBPb1TYlXkhtsX+deW\ngku1/ECjhTB8lvwrQ4OnBGNXWD18UXoCjPY3eWJbs2V7WSY/XTvQoOeU2RAP\nWfWY\r\n=6MOc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCa0fSWUJXV+PJVsFHTe3kXpV14WrXJF79tjlhJXuK/egIhAIla2+cH3c8l4Tb/8AUtfcmej+wV864fky/tQ3RxNk1I"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkuufn5p.0_1612684059887_0.07897462810029587"},"_hasShrinkwrap":false},"1.0.0-canary.kkuvw4g4.0":{"name":"plumier","version":"1.0.0-canary.kkuvw4g4.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkuvw4g4.0+7476186","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7476186a434ee87f18727f081871e74cb0bbcaee","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkuvw4g4.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-ORji4hS6IqatfO5caxrrhggqpWHd7lUCKf+bnSGXYGSjcn0L6VtPksh1xDVlXF9KqL9n6517bB401ru5Ih0S6w==","shasum":"98da639b86022bc9f89d134be18d58e7a5d7ba87","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkuvw4g4.0.tgz","fileCount":13,"unpackedSize":34300,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgH6SnCRA9TVsSAnZWagAAmjAP/jp6+gt1JS8gbG8pzK0f\nAcVF6BMeRfUR6QZc6dyBmhENozsrGCZSCC157/uNEhSKfmEQC62Z16CWZnE1\ne3VFxaZyp8ezVydHzmXYL6V1uteA64+8e+FpVzua9gWUYykDFBWchyu0vqJ/\ntQjoDH3W9hZGjCBv1Enng7QBoHOvEgtu7ZdYk3kNdkt4Nkvd+aieQwK3tHDS\n49DGgKZCxfrfNuqLM5tqUYjW68UcpQfos6SLHf/xSBik9G3iYnLApzPnrr9K\nDT+PEzA868tUAGz3K3SsSVCjMB3SLvo5V24nR6qR9AFOxLK9mgvxiIwW9tTA\nceBgX5Tbi2eoSQoc3qhkjtq1n6CEwKK4GGWNsuqPoPPfqNWr1VhasQlNvee2\nyk5EvhdNbpqoq3kx9iB/CMWt9JeZYte19qf8rQN4NeJdNPXfN5NvyiNWhgF1\nZDDhP9QZpiCS5Z2cG2wvp0kNzLgZURb/XpT33rylgwXauy4+Fu2oISBexVCP\nTVl1j5pXIolucTuUrz/ZMAor93pzm8YVp3LiJR0xAM9U4/N0Q/BipNU9KRIZ\nmsuFDBI+jYGAvcNpwxpvknkZ7loSfGbGXFlmO24O6ZyBZdtAO47+wxosfHZT\ni/1O2qzkH03sIYko6sfDZYHc3BY+8BKkQacsUDdbvXYLDFOFRP+URB/ZinmC\nX6aK\r\n=OwyW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCtk08oUc0vwV8F/L4D9s+OHLCBItLww56wP9OPGy0x6gIhAOeCSfNp8l2076UTfHNn5DyvX9/nz50s0KRI9Xu1EGTc"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkuvw4g4.0_1612686503280_0.5222104766307749"},"_hasShrinkwrap":false},"1.0.0-canary.kkvui0xl.0":{"name":"plumier","version":"1.0.0-canary.kkvui0xl.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkvui0xl.0+134b33e","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"134b33e3ff9abb0c01de3da41fbcef23a260696d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/plumier/plumier.svg)](https://greenkeeper.io/)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n![npm (canary)](https://img.shields.io/npm/v/plumier/canary)\n![npm (latest)](https://img.shields.io/npm/v/plumier/latest)\n\n## Motivation\n\nMy subjective opinion about TypeScript frameworks nowadays is most of them are too advanced, even to start a very simple API you need to prepare yourself with some advanced knowledge of Separation of Concern, Dependency Injection, SOLID principle and many other design pattern and best practices comes from Object Oriented world. \n\nMost of those frameworks take advantage of OO fanciness, where framework provided a mandatory rule on how you should separate your logic and layout your source code to keep it clean and SOLID.\n\nIn the other hands frameworks doesn't put robustness and secureness as priority because with its fancy separation you can create your own implementation of type conversion, validator or authorization on top of an existing library such as Joi and Passport. \n\n### What About Express?\nI am a big fans of Express, I spent years developing API using Express. Good parts about Express is its simplicity, its easy to master Express only by reading the documentation or by spending a 10 minutes tutorial. Because its only consist of Routing and middleware. \n\nBad things about Express is its getting harder when you get into the detail. By default express doesn't have a built-in type conversion validator and authorization functionalities. Thus you need to combine some npm packages to do the detail things. You need to configure schema for joi validation and mongoose, setting this and that for authorization, at the end your code is so far from simple. \n\n## Enter Plumier\nWelcome to Plumier where robustness and secureness is mandatory and fanciness is optional. Unlike most TypeScript framework Plumier focus on development happiness and productivity while keep simplest implementation robust and secure. \n\nThe main goal is to make your development time fast and delightful by providing built-in functionalities such as automatic data type conversion, comprehensive list (40+ types) of validator, authorization to programmatically restrict access to some endpoints and more cool features such as: \n\n* [Parameter binding](https://plumierjs.com/docs/refs/parameter-binding)\n* [Route generation](https://plumierjs.com/docs/refs/route)\n* [Static route generation analysis](https://plumierjs.com/docs/refs/static-analysis)\n* [Meta programming on middleware basis](https://medium.com/hackernoon/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n* API versioning based on reflection\n\nAll above features created with dedicated reflection library to possibly perform rich meta programming on top of TypeScript language to make everything feel more automatic with less configurations.\n\nFurthermore Plumier doesn't force you to follow some design pattern or best practice. Plumier application is highly configurable that make you able to layout your source code freely. \n\n### Robust and Secure\nPlumier provided some built-in functionalities that work in the background to make the most trivial implementation keep secure and robust. \n\n```typescript \nclass AnimalsController {\n    @route.get()\n    list(offset:number, @val.int({ min: 1 }) limit:number) {\n        //implementation\n    }\n}\n```\n\nAbove controller generate single endpoints `GET /animals/list?offset=0&list=10`. Plumier uses a dedicated type introspection (reflection) library to make it able to extract TypeScript type annotation than translate it into metadata and provide functionalities that working on the background. \n1. It automatically bound `offset` and `limit` parameter with request query by name, no further configuration needed. \n2. It automatically convert the request query `offset` and `limit` value into appropriate parameter data type. This function prevent bad user submitting bad value causing conversion error or even sql injection.\n3. It automatically validate the `limit` parameter and make sure if the provided value is a positive number.\n4. It taking care of query case insensitivity, `GET /animals/list?OFFSET=0&LIMIT=10` will keep working. Note that query is case sensitive in most frameworks.\n\nPlumier has [comprehensive list](refs/validation#decorators) of decorator based validators, it easily can be applied on method parameters or domain model properties. \n\n```typescript\n@domain()\nclass User {\n    constructor(\n        @val.length({ min: 5, max: 128 })\n        public name: string,\n        @val.email()\n        public email: string,\n        @val.before()\n        public dateOfBirth: Date,\n        public active: boolean\n    ) { }\n}\n```\n\nFurthermore Plumier provided built-in decorator based authorization to easily restrict access to your API endpoints.\n\n```typescript \nclass UsersController {\n\n    // GET /users?offset&limit\n    // only accessible by Admin\n    @authorize.route(\"Admin\")\n    @route.get(\"\")\n    list(offset:number, limit:number) { }\n\n    // POST /users \n    // accessible by public\n    @authorize.public()\n    @route.post(\"\")\n    save(data:User){}\n}\n```\n\nAbove code showing that some authorization decorator applied to the method to restrict access to each endpoint handled by controller's method.\n\n### Useful Reflection Based Helpers\n\nAnother benefit of using reflection library is Plumier able to provided an official [Mongoose](https://mongoosejs.com/) helper to automatically generate mongoose schema from domain model. \n\n```typescript\nimport { collection, model } from \"@plumier/mongoose\"\n\n// mark domain model as collection\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n\n// model() function automatically generate Mongoose schema \n// based on User properties \nconst UserModel = model(User)\n```\n\nRead more information about Mongoose helper [here](refs/mongoose-helper). \n\n### Reduce Duplication\nThere is a best practice spread among static type programmers: **Never use your domain model as DTO**. Literally its a good advice because in a common framework using domain model as DTO can lead to some security issue, but this will ends up in another issue: bloated code and duplication. \n\nPlumier provided an advanced authorization functionalities which enables you to restrict write some property of request body by providing `@authorize` decorator on the domain model.\n\n```typescript\nimport { authorize } from \"plumier\"\nimport { collection } from \"@plumier/mongoose\"\n\n@collection()\nclass User {\n    constructor(\n        public name:string,\n        public email:string,\n        public dateOfBirth:Date,\n        //restrict access only to Admin\n        @authorize.write(\"Admin\") \n        public role: \"Admin\" | \"User\",\n        public active:boolean\n    ){}\n}\n```\n\nUsing above code, only user with `Admin` role will be able to set the `role` property. Using this functionalities will cut a lot of bloated DTO classes and duplication, and make the security aspect of the application easily reviewed.\n\n### Lightweight\n\nAbove all, with all those features above, Plumier is a lightweight framework.\n\n```\nGET method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       GET         32566.55      0.00\nplumier      koa          GET         31966.55      1.84\nexpress                   GET         19047.60      0.00\nnest         express      GET         16972.91     10.89\nloopback     express      GET          3719.80     80.47\n\nPOST method benchmark starting...\n\nServer       Base         Method         Req/s  Cost (%)\nkoa                       POST        12651.46      0.00\nplumier      koa          POST        11175.10     11.67\nexpress                   POST         9521.28      0.00\nnest         express      POST         5251.00     44.85\nloopback     express      POST         2294.00     75.91\n```\n\nAbove is a full stack benchmark (routing, body parser, validator, type conversion) result of Plumier and other TypeScript framework. Showing that using Plumier is as fast as using Koa. The benchmark source code can be found [here](https://github.com/ktutnik/full-stack-benchmarks).\n\nCreating lightweight framework is not easy, from result above Plumier only 1.84% slower than Koa (its base framework) in the other hand Nest 10.89% and Loopback 4 is 80% slower than their base framework.\n\n## Requirements\n* Node.js >= 10.0.0\n* TypeScript\n\n## Blog Posts and Publications\n* [Reason, motivation and how Plumier designed](https://medium.com/hackernoon/i-spent-a-year-to-reinvent-a-node-js-framework-b3b0b1602ad5)\n* [How to use Plumier with mongoose](https://hackernoon.com/create-secure-restful-api-with-plumier-and-mongoose-3ngz32lu)\n* [Advanced usage of Plumier middleware to perform AOP and metaprogramming](https://hackernoon.com/adding-an-auditing-system-into-a-rest-api-4fbb522240ea)\n\n\n## Documentation\nGo to Plumier [documentation](https://plumierjs.com) for complete documentation and tutorial\n\n## Tutorials\n\n* [Basic REST api tutorial using Knex.js](https://plumierjs.com/docs/tutorials/basic-sql/get-started)\n  \n## Examples \n\n* [Basic REST API with Knex.js](https://github.com/plumier/tutorial-todo-sql-backend)\n* [Basic REST api with Mongoose](https://github.com/plumier/tutorial-todo-mongodb-backend)\n* [Plumier - React - Monorepo - Social Login](https://github.com/plumier/tutorial-monorepo-social-login)\n* [Plumier - Vue.js - Monorepo - Social Login](https://github.com/plumier/tutorial-social-login-vue)\n* [Plumier - React Native - Monorepo](https://github.com/plumier/tutorial-todo-monorepo-react-native)\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn `npm install -g yarn`\n\n### Local Setup\n* Fork and clone the project\n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Build the project \n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n* Process will halt properly on the `.ts` file.\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kkvui0xl.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-VS3pZW/l8sKiHaPyCI8KFux6r8WFIpV6ofv3ynB4ONMZT9ZKiyp44xuYChdbiidPbuyYC4XRekJoYrAgDALxyg==","shasum":"fe80c8744c75f397d692ce21f77191b27e93c822","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkvui0xl.0.tgz","fileCount":13,"unpackedSize":34311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIIe7CRA9TVsSAnZWagAA/TIQAJ0E1BtJ94I/Y790Yt+p\n4hMvcqtfTu9lucnDNSCYVgTX7wLFc0C/DgzcKnQ6LWZFePzI9s3YjkaqZE2c\nT0KJyhMPNVxkMtHo1RN9QnvXySoS2SXXO/zdcRiMnjTYd+4vmZg24/Oqb6DW\nF6EQJdcoyPWuLs4OYy3T87PwC9V8U4xNdakKI9I6rtVULsbU/uSpaFaAEpp3\nyw8j4eJvxRzKOrbn4FN6WuiOuhVVZiIFckD2X29hdGHbCK0EeUd+y5SH/pwj\nYrxLLHXXQqmolM2KWM0p7ZJn2D2Uc8p0H0FE3UZr/ERfYYHgqOiQXy1fA1Rn\nw8eCxqTR0/Bbgtq0eSwgces4DGFqJ7hWvutjMmhP7w2owEcLkGYB7I8Vcc/0\nMAEmPbS4x0J8mmLCWI48f+jEhSlJrQTuEgn05+M0LUQ6u3X/n0Ly+PynTxg1\nGe4jyULuhZvi4OtvHRx742WcJrRO9LYQyqNG9rQNmLbuBN0yeVf8phO5hKxb\nx7OQ+upJcaMKjsek96I0AasfLajSR7t7+QV3jhiLnuFkxk7ofgYWWuGyN9rL\nIp1PTvBA8PEcTZK0kv9cpSaHh2YSPUy+t4zDQWk+FzQ1SKIm/YxIL/cWVsIR\n0hHZshfhL4f3t76Vv8xwHRHzGKygDHfCZpXfJMfov6kQMSqXp4cof32XqlHk\nUNrW\r\n=jr6R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDx+h1NPrsp8ATcEanIjcyAa5uUGUN3Z1p39yh6WekHeQIgFUdqHDH9rb0UWT0+s/lVZWLQ4/l79GhHCMsVgEwkM1M="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkvui0xl.0_1612744635097_0.2792889123894182"},"_hasShrinkwrap":false},"1.0.0-canary.kkymfdyo.0":{"name":"plumier","version":"1.0.0-canary.kkymfdyo.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkymfdyo.0+0070e19","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0070e19c3fb93792309758b1680968075f22ed35","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kkymfdyo.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-2Tgh8VNAwffaG3KzL4yLWU9rHElxgM4LPr+1D25w4AsFnZf4bIcN0VKCIbezNaPPm89HSKTjojhxh12mq4pmZQ==","shasum":"bead8c2f1bc33b0397b4fc3c3091525d552b6c8c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkymfdyo.0.tgz","fileCount":12,"unpackedSize":23346,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIxdYCRA9TVsSAnZWagAAK5MP/jClm3/loppoNxcOe5pT\nf0zbJ6xN3RS3yhy68J/BnX7nd7vHdrrExijGJVWf9A+eR0jrAJTYFVP7f6Hm\nEp2jFhnYvIaxmt5nc/B2okoo64bpJPk6m8TMsRCxD6Il0jzLWoIOyHm1ZYX1\n/t/xAgULAjCjT1qbcsva1URNWo682WTztBu8JcstWXps+kLNkkNAmUIW589y\n8FR546vqDlpI5ZOHbYZy7hrGfo2Fn1O2A9XlGKdOfXbBXUeQQgvkM/3tJQrs\nmEZBehVXIf2s0BWOkIUSS+F9h2lz4YjOpiXeJ1agj1O7tx3Ve296IU0Da447\n3GGVUTyuUU8CZOIYUt0uP0LOtnDaDPnbN1EBBJ8UWX2bQYxz7h/6E+U+625v\nSkp2LgtCbYKTCRmPYepi1a3r7+fGQb9+dBn5Sgf92nuUC8OCCOm1JPitsx8c\nXeqz7oMhkMWOcSa9yveErw14SL4vt54OWyYZ4zC7VxsWOIxeuMEjDACjOk+J\n1b9E95RlwFHEY4dIrz/vVSbcyvl7Tmo9yWShqB4ZryA5ZLQ6Ju3rm3dBKPyB\nbDH0thBdEYF0kHZ9qiFIJ4lnmaDWuWYbekKlOfmiwPLlGDVQPFjwR4Er5vXi\nmt6hnAoE70BxYfpuL3X5W/zif97C2Pq9bttXxY3JAu5ak1ZYHZ8Ud9hzshB4\nSxBn\r\n=YvpV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2prdxKxXl9gYAo5TOegYwjVllVfU9OoEO7ldDDpV3VgIgd+nqwKaAc26a6qcKbS7thF1qxaA8zchDMZAShvyr5mc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkymfdyo.0_1612912471903_0.990718102713243"},"_hasShrinkwrap":false},"1.0.0-rc.6":{"name":"plumier","version":"1.0.0-rc.6","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.6","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c58f8f08dd74e57f5d6fa6ebdb198f57beaa1f39","_id":"plumier@1.0.0-rc.6","_nodeVersion":"10.23.1","_npmVersion":"lerna/3.22.1/node@v10.23.1+x64 (darwin)","dist":{"integrity":"sha512-ntqVYr7Z3PTz5neBfXkjepkm16mTeK7FNBytOHMnfnNrhY7uTDSEqMN+TtAT2LkUWzjxefIOS+0bdWO/WbBhcg==","shasum":"1f9fcf756f8e6cdc5964b528ffb2466cf1250aa0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.6.tgz","fileCount":12,"unpackedSize":23304,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIxeqCRA9TVsSAnZWagAAN0UQAKFcZw4VIh/cK4RQRB4u\nfgXqDNMyR5gRKBzIuI/+a8feV3t/kFYCVER1U3eDYMX+onG3V6wWANFxkmXq\nTlwq8Cc4QD0llFZ3U8zjE7K37GqLrbDzinS7YyL+/qKKb7IoHFscpA1F7dUZ\n8W92rKV9fhnYy0g1VYH8S77P+3OL227Gh2AD5LQrJDfpmaBnbQNVaLD9527m\nbkOd2KsxOdgu09Ns77KnZFKefwGJhKv8aPMtyiYzPe7+E2XOzNtJ5ZX9RNp+\n68TSp2uYVu4tWrLHJ/fIfbF+S8fvbDEbsq+cnV0AEqkr+9BNkOa8iRcq2I9P\nOcrG9la3xSJTcr7vzs/t2j+zofftHXQuwURDsPIKMKoGZAKHStXoQsjhEjmz\nQXY2at/0Dv1zu0K/NdFL5PkuSyeTW6bVnpFMExzv/5owOdXEeB7+YMcqG45d\ndx0TrKSkEnPhMDrV0UwHAsLzvxxfpbW6CzmZSyq3+ofKzUEd/EWRM4eVFszW\nFNB2Tv629tQ8rPdwsYjsMI0Vrl9iWBAC33HCdLBQDixrpqLJgLRAaZiOxpEm\n56xzNw7WA+8SZLdJm/M/BRXtY8xsPXv6mHWGXYEnbbZExRyWBbZpxlPzuenm\nEIc2KgL6qrgndwmSbkG7XSChsvDMyuaOX0Fe6VpxTvhJSrcZENgo6Xd7EY+S\nR7fe\r\n=fC2H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICQX2uNFlfSMQew3AifZDeP85Dyxe2I3UVf5Dv3/RRxQAiEAoq5Lb+YLbV7joVjd/AbZzjs1BxLeRElVLjzdIy+5l80="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.6_1612912553761_0.4932541161228501"},"_hasShrinkwrap":false},"1.0.0-canary.kkymmnlf.0":{"name":"plumier","version":"1.0.0-canary.kkymmnlf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkymmnlf.0+c58f8f0","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c58f8f08dd74e57f5d6fa6ebdb198f57beaa1f39","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kkymmnlf.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-gQFxy0h9u+r5B5YnCsA/gPtV5V4B8RgiO8rWpGNRa3pJfD1DnlTlKDG6w5n9f2wzEDvJtL+1dk5m3x+lFKMHDQ==","shasum":"784a85a34953a6823853353e13b8504a90dffef9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkymmnlf.0.tgz","fileCount":12,"unpackedSize":23346,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIxiwCRA9TVsSAnZWagAAhVsP/iMCCZFNDWECfOSxrBnQ\nxJctge9C6AGGLMFG0hSyfIQv/LBp7FLe7gWMU4+coOGjJ0O4BQBvNpeeayDP\ngK3Kb3DpzogmbQzbNMt3axW5ZoMro3/4eNbvSB3UTpfrcPv5Rmc2en/WA4Na\nuc8dlpNApqFX0qTMFpNR1ifSxV2pF4Ah1ARr3BuMC2kArDnpAmlCkam2jaN0\nzHgKNG8j/3utim6Z9/R/VAMq0wLbrDqijJMVL3UbiGB2yAl8dziWWfIUhjjV\nQnd6+Eub8AZvMXUMEiv6CkycSxM7pkbenCxqPVoE9QCRUlABueuGkQD3cSZj\n8X2+VwnnA/Cg8GV0WmT2lxukkB6IPeLg6sH5qTSEjFgmJH7WIvwjGGDzzNey\nqu5r3wsoVz8Nqp2ofokkiIQj2rbYYC6zdRS8VG1g724JNDNCnTjKWqE1t3I5\naCbamFH798Wen4e5K1+Md7Zdi4lSslDU+umgcMykZtDo9WBHWHXL+/WoMAf3\n1gZjTU57eh1qcLwts7MVR7EOSZYGyuwQs2CmN3QCo4MFbNtzxJb6ZRVkL8wR\nAFUFnHtSjPshDgYj2yF3PQqiE2fWWNEfNaA3FbfWKShpLjvdgzb3tjErrPfp\ngLII7H5ac2ltxmjiSDrMODus0jXYA97quNPQBiwFMjmoooAk/7u4f4KbbKYf\ng1t4\r\n=Wcj/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDeg+P+4xdCQRP8+VHKQXU8uRmM/YzZrRZwBpwhQjoteAiEAwgYQn++or6GkUn0S/2K8c4/SbMFSqhln8oUfCfzaJgQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkymmnlf.0_1612912815831_0.2265100236226263"},"_hasShrinkwrap":false},"1.0.0-canary.kkymq36h.0":{"name":"plumier","version":"1.0.0-canary.kkymq36h.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkymq36h.0+5709c5f","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5709c5f0d9961db32616200da37f08b15fe87554","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kkymq36h.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-vByNXK9P+YwEt7vnPm37HmdJlds/ZPhaRUubfHUllFXtF5nwJaBrSf+L23aIOMjmwVr4O3/cO8QVXvcNLhYvrg==","shasum":"dfc114c12dabf473af18e11f4afc404612ec0cae","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkymq36h.0.tgz","fileCount":12,"unpackedSize":23346,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIxlNCRA9TVsSAnZWagAAZAEQAJJAeMhmCG0bO1MddR8A\nYRhtA8lq+GmtxFTX0KTCuEiInJDnn8Y+GnnUd9n2+LKR0Ks96ZLy9sgYNm1y\n3ftgelSM+RsqHmjVbBzwCYDisEZxsR5Ry4bWv9GcPH02GQi0lNDr0kRP5BNs\nv9Uuby4NF1psA5dmVP+vmBV6SjHoyYFkvL8BZa0DEgoZnl2sygxHj9UFKgEO\nBv+nvyb0h9c/ta4gJqmitv8WRGGp4A11YYoIVrQX5zW8YoqaKs4pKlqR+HPg\nCaUF7OteONGP0za/etgmG+oYr2b+O8AYP95ECKpgUr6XCOpDCb3DytWSpv6d\nnTsVsTv+TNxI8w1UGIPe/TAmeFOGWjXEPATNXLiRg1PloEeRX6n8JGOIJThh\n1dxasaWSwPpGxDON4aFTWNv2pt1PbTfmJ/Dk/KuVZuA4RCdP1twdMft5hr71\n2kUwH8rEqy5tLLa16x6Hf454QlmdXdeg2Zmx/L1OWu632KcCJu3KqD+dQgjl\nYvHdUGC9zejP4e6b60pN3E48N3MmZxXxzAaMQhgE4mRSqfo5Y0eQoVkRQlqH\naeGW58H5739xtavuDmHXw8XMxm2uiAu97Kg4IzIy5XfNidZ88guYpiM0wF5y\nDr03h3knK3DPZb0Z4U3ODiCY+cY+p51xwqqqNY4Sr3fmYBcDx4UlOwSqgfiP\nlCay\r\n=bEei\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDFY0LWfFLUTeKXIk/sxxM2/4Mm2sM0Wp+KjewmOe17gQIhAI5I2al5P/Wv5NnW6r1XQx/Kd0gM2vnDAJOhxpBvv5MJ"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkymq36h.0_1612912973206_0.8013694274355547"},"_hasShrinkwrap":false},"1.0.0-canary.kkzy8nhn.0":{"name":"plumier","version":"1.0.0-canary.kkzy8nhn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kkzy8nhn.0+9d2e661","@types/bytes":"^3.1.0","@types/koa":"^2.11.6","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9d2e6619731e9542af7104ea67794d0e38aecc35","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kkzy8nhn.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-j6slzASfhynkRnFSUvTBIhAuNXLLh1B4bwzo/oULCyjqmTT70+UKWqYMKYWrcOlT/0gioNpqpyWMBnnY6/yKWA==","shasum":"14b29c1d10f779844d9b0b045b1d9f705c1a97f5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kkzy8nhn.0.tgz","fileCount":12,"unpackedSize":23338,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJFELCRA9TVsSAnZWagAApC8P/2hAX5uqoBSiliSbHcpG\n+JJjEx4O+YtFnYgeIUonmxw2+kAnBcgvWs6gKAwIyuD5Rmx4P16X+IgRDnC1\nLsrk/FXFGBrwJmVV+i63YlW1pHWTKoUthHQQGaj6UGBrNZUk+f5Of2ObRmSh\nnoxtDT7Yq43Zn23QrvLX9mb6JiwewAd+G9IbaFrHIBBk7KfMSFZDTdRCDATI\nB0a8hMsfsqg6ty394SrOQjVTPhg+hEyGCmvUXSXqhNy1Ha9upLcb8qYfptqE\nIQoHIl5xGUtinewQrqxk09/xyf3/Bm5w27eacOsKVApl/KC1yGdov0XAhqaW\n/E1+A3p/IZcrhN4UiJ56VON6szfV/L0I4QSvX2xifLwy1bo/GZQVzcRGwB3n\n1satybOqVQ+CNFEmE2Gzi8wVq75HDGZvHhqwLsEpKehOzGtBww49OYRRBm/3\n1evRuahjJfwxNKfNrW0A6MrTs33YGqXiIa68wWJmdA8TGGVZjueIyOGKxVrP\nANGrthJgaE+PmtGYoddqbFawq5teGd6NBYgz71fzJxIYnVKCKprXomqp6hr5\njOOFkQpEjtMJ0+pZ1pvVwiSWTK8YRcJe+kVeksrUDuJHE8lTRzBn4v+5Hb8K\nctvRIcPdl3oijEg/dybEBPnpdWQ608NODD1gMVGD26vZaxYZgM0Exp5Y2hvd\nm+nU\r\n=/xsQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGUFz7wFcbYY7orRTf5mbPBcvdcZNxlSKHb4OYcM1Ug7AiAXBEVDr+kkchokEieFpT9c1RutT4bV4tkIs2UeEO/Nww=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kkzy8nhn.0_1612992778743_0.5765117398153201"},"_hasShrinkwrap":false},"1.0.0-canary.kl05lf66.0":{"name":"plumier","version":"1.0.0-canary.kl05lf66.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl05lf66.0+f79e1a4","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f79e1a45d6c24b9de61c0fbc653c4776888e8e2c","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kl05lf66.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-wp42Kl5IElU9HVIMFAJk+4bFZMyzdS2kVhxTddTThK6eWR2aEnCL5bUN6brEvB8fXRoZ2asx9HAhSb1J/vBN8Q==","shasum":"c432c870ea33d92047bdb7da0c19c5b792e1caf2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl05lf66.0.tgz","fileCount":12,"unpackedSize":23338,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJIFICRA9TVsSAnZWagAAlpgQAIfWNDXdfcF1czNcSDXI\n7y/VKxXZDKuSkvHrCCdz3tB7zUnrPWQ/LrgAiMNb+r92V5pfxlizbNwf80Vz\nm24srOBp7TYnPfFB7pfuQ/EEUFoclRR5Dcwxr/2a9CZ68x8sDDj2q+wRZqQM\nIpNlaAkEYGpdsDfCOavWHRHLgkxLdaUQXhb8bCkLydVIs0eRsG9nSXur42c+\nFQGUcYoVxuJmyh8F7cNeA4bQ9ZtHtTwMOAz0PgINzsbEwlKlf8lUOrCoOg95\n0kv6Ny2BdwfbUKZ9Or0w9E0hxOPgrer6+8aABhnSm/cqh8mGeT1miAxEbFaN\nixa7SA04d/7YWRdFQkGw7LDrWIUWGr9e3LMV+9uRwepx4Z30/hG4W0YdVAGK\nILnIc3vqZf/wcKOzYCRvBabLdbRKc1HQXd6PlsU6264uCKQEotwR6hH2/30L\nyP5FPLlc+h/49pbsxIg/bQi7kbIVmKAjgQ1ibjhs4DlRHcmN1wsxmsSPbQQl\nh75o4oykduPdRuyAqiEfvG90lTEvRVi24DWqiXYvKvsiwUZyuMzx4a3aACAW\n+U3KG2n+KcrzIXbdop1k7aZrtnE1rKdZhwPkEDVDalQaVlMSvi4QzY6Zmolh\nh9UjhSB2L4XgZ5rPutr960IK240rtQpnYkn5OKYyfdxdrgmfW72N7NfqXagc\nBBtk\r\n=S54O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID9jdMm8lV7NFOlCrhrxzPNdhabvE5X3v7Mc01mP3tz3AiBeVunUkzrKmgUj9Ht8yEaPZxICEuNg4e3PPRKwNciCDA=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl05lf66.0_1613005128212_0.6215358076377939"},"_hasShrinkwrap":false},"1.0.0-canary.kl07x6lm.0":{"name":"plumier","version":"1.0.0-canary.kl07x6lm.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl07x6lm.0+7cc4e7d","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7cc4e7de899ac338902d2eba51238712f9946be3","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kl07x6lm.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-WGxAgA53oUEEfTGqOt6t0hmte8OVesHQG3KV2LRko0SOS//18cWa/Yz3udTSWJVqLKpPm5O7zQPVtytZLHZp+w==","shasum":"c6db9acd1934391b665cbc55e0e0a30ce360ad45","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl07x6lm.0.tgz","fileCount":12,"unpackedSize":23338,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJJCSCRA9TVsSAnZWagAA6pEP/RYs7My1hnouHOaOJx15\nL6A430Ulrev8K+S42Mf4I7P3ZRh2yqcOaXFTWXWfOCWrj+GsVCe3BF6ZGuIa\nCa6VPU7mNaE5Z7mfs/8RyKVE3m59Bj6qWzn8KVwykCE+TE2/zPnczN0wI4fu\n5oftN0djxAwg3C2ief5TMu2HWfnnCQrEcQWEBUY5whYkJzjUjBHjTUO9sNPM\n8fKEL+wYOQe/tn0jK/fWtqAQXsQZLFHT4qt15wizbF54O4bJ57vJ4kPeTTVs\nUM44gfCPUJh7bk7pr0P35JD/51EfDc6eHaytdkGBfM1OroveRsJgrNPwvFEp\nWksf1U9K1wjltTcPpFFVh76foTWhPhxqVbu3RGGbXKgeuuDVkRUJqTn61PTx\nyDQ7N4kepgZvLLJ+lKQT1SrEjAUjUTuimktDe3YjDdHp6i9GouEwZeCMsKLH\nLDpiiv0naeI/m7DJ1u+KPuuJ6A7pp086wflCHmP4UD1SzG/r08IkYH3s1M7C\njSVoiSjBxSx24ZKBfgHVIKY3C2zPpYxKNf8tuoOlucLAPRjJ8bYPJHtRABXk\nwOIwn1Nqtd+/EiAqGr3BY3drIUNhzVfTknflooFT4vH5typt96cE2cRSoyPN\nB6lbYw7zivBkU6gjTC86ZmeKvQHePRoCFkjjvGXVELPiwHMOQoAvSdB4iSu8\nZtne\r\n=/YHE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDqRAOYfNRdaMiKV/41D4Lq4Qe6AvXLrx8u6tnverEEgIhAOM5ZcfxT2NS+fjZ1l/I0U50D9DsmhnYF6ffBByTrpnK"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl07x6lm.0_1613009041776_0.5184636937932392"},"_hasShrinkwrap":false},"1.0.0-canary.kl0bv9c3.0":{"name":"plumier","version":"1.0.0-canary.kl0bv9c3.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl0bv9c3.0+4b534c4","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4b534c4a15909764a5e139bba0c8ed4e7ddbf717","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kl0bv9c3.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-FuZZt/b7jwswrNtasMg8oBdWXHnLSnABoXmRme4VPoE68t9M41npDH3sEd8st6zsDomOypWoV2R8gaifvxHchA==","shasum":"784b88a67558e246b1cc8912afa8d263fc3ba2ae","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl0bv9c3.0.tgz","fileCount":12,"unpackedSize":23338,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJKqJCRA9TVsSAnZWagAAYYYP/2IjoonjQFexEHPbPBOP\niqdKCPYabIwG9e4ttWG8rEn0R2uthS/YncmmXTE0Qgr9EWbklZFHxmY4fTZf\nkVnxUmeOb5hB07lmsKlICb4JG6hDGX7sEamlw+4VTAOnnHzRStbv7GIxdF1B\nbg5WU7YD6WJletLrb2cD+gvM26IZpSZif5KbwkhxginKAi17CshO/eqPn+PQ\nmXuLDkBLzVJQIdW7bpbsZcPIOhnk5dgpeAtSLEOjT+R1u5XkN1R878BuuTDq\nUwO6yvF2CP0TFN9isbcAijG8EgUtxhp/T+LQ5/oSWAzn9ElrH9eIpUKNTRNn\nxwE5IkffUvo9X06TBMKl1a34htdOnXUKFFeYPzEseUy69+E/xYMbX3VoEo+u\nwVQZcDBoSq6PnoCWcAlVmbJCxeqNJkVmg+JjWtDXgapsmkTgGZ6rV20euJcN\n+Baz14EsPeJd7Oh3Utum0QljA0pZVYxtlQ0pB1bnXYxpiQOxm6n+UJtpK6N2\nt4pMNEzx1P2a37MuN9J91vnliX01CsWZfcb5objFg38JGQfdQq7amu3z6bvP\nQjCU0mdI9HCY6jQvJA4Pl9cX/Eu3zsFBzQYwUvpYWWG7xpUf579RCDVtjmKr\nGTSLm2ON8YsB5mSsysT5YS3pYsGWpXbkg8xiHdj4t0JAFYUDMoed3p5fHlo9\nNlKz\r\n=W647\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF+t5x9fCsItqbWp5jz1+yvy4fPii7FkJ0tbswtXExmBAiB044SmYpsKGOraEThqRqY/ZL06sO1TW1bORRFlz4eu8g=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl0bv9c3.0_1613015688973_0.48140980434335434"},"_hasShrinkwrap":false},"1.0.0-canary.kl0bxs5i.0":{"name":"plumier","version":"1.0.0-canary.kl0bxs5i.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl0bxs5i.0+5549c1f","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5549c1f8f43fc7b50e2185ba15ef531252290b2f","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kl0bxs5i.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-a8771GC2jpB5Y7TYiF60X6eMv8I3E2kkRb3GtzvEpnjnnP8GyyKiU0IYmY6dg9QgnxxqH2hXVw2nCsdL7wPZXg==","shasum":"aa821faf820018bad7161d72d79cb75c3f292e76","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl0bxs5i.0.tgz","fileCount":12,"unpackedSize":23338,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJKrqCRA9TVsSAnZWagAAA/kP/RBF9URZR+8R0rhXx/sL\nzIzTLdnr0kiL0K0pHdFHuaeQ4BXl3Fag6jFs2nfs2n45GpxkYu0g1Zz78FBg\nIqZRtGjiZlzLQt7gviFEc7i9aebHtPoOrA/shLW9pRckhbmqh6G26S6bQtIo\nhTT1W0zrzbhpvrsopYB3DQgZWjcIVhZXXv1CSxNhaZqpHh5JHsXYqvft7pai\nBEW6QkSixcmAqCKEYjXum2pXIAk04IFtwZdNeiqNzIqseixu1Gj7g9rHzVXJ\nIwu3xlZ05fWhKvR0A5o46nFx9gmvXTWEu/HPHcXK2YjGYwExACpaHE/ftBHo\nj1HzHvVNg99Ga9UBjmPx0AkB9iul5USgzzThF3ROjJ5ximm8tFdVQW4cmRy/\nNsQyoBL4eCxK3J/3k/xryeQXb05no0ZeBAsEDMcG5FDlgyAS2iq0wR6CL3GV\naAni9Hva6H6ikJIWdX37eDGToyZfY2EuQKNGVN8JHYkBmgC+ZKlD7jizJ5CR\nPNxQxoCKG8Z2GtNNAZ3vbMrN+Vx4AuS+J/PTKKhp5QM3acrTrE8qM5GFWiID\novIv5JTbpn//hC2EzWzUeycHE5sJpmPSXXXbrJGRvWxu3SHqyoJYwkRZzvZg\n5XePrLrMf+AFv+SvdIk3GfhrENsGLwdH6peA5FSIfrs9JHt0hbYtZKecrf48\nO1ik\r\n=D0mj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDFJRoS112akfH8Q6kO0g0R+1Wq3DGO7hMOKX1nkSDnbwIgNTSz3w176JEzeA7n2Q6b4xTq2Vk4KfsSDtoKs8YvB7s="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl0bxs5i.0_1613015786304_0.44725358363229617"},"_hasShrinkwrap":false},"1.0.0-canary.kl1fczf8.0":{"name":"plumier","version":"1.0.0-canary.kl1fczf8.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl1fczf8.0+6fd3317","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6fd3317273b9c3fdc03abe36e47a30f704ffdeb9","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kl1fczf8.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-7iAzbpas9Dcv59FblH3g0CmSrhdy3QDuzWFTuPPZHUJvsMiECTJKEbaHbLONjv++6FTZ8g8GeBdDL+4cvIVX0g==","shasum":"9f612d6734777d7744727135545372565d8c233c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl1fczf8.0.tgz","fileCount":12,"unpackedSize":24004,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJa2UCRA9TVsSAnZWagAAVFoP/jwogtWb9T7gghJit89r\nKsZEPNg7ZOWYRzHbSbYYvwbzwaOrCrcA+tyji3DizkKCJ4wC1xHPkzLgwhho\nX/mqxHR4ONMLA8f3+P90hxxVHtqVA95aYSf00klZUn9zGDI2dr5dnOn8FmZQ\nmjzdjUhw1X6LG1BbocX4mG1NC7TlrZil2qsVNJ0YfOmm4UCEiHJOGeL6Xb64\n3OcH8S3GFIKqasgJaXxsgQzmuxLYKvTBr/oloK6rXrgOsHlYUrf/2cHmHxEc\nP0ImDm9WlcgIes5PJ46GQ1g/CVvo2j5OQEK39S0uH8ulokY73tiNyCVIqtg3\n4IwIpl3qnRl5VYcWwFp7mtBT+xEgGJ0p06hJVRyESbs8rFwx/JxGw0vrAdeA\nrCBcy3VAFQ0isUIoI6tYNAgEImAtay1XZOTQTLtpypz7PCaWzIEsj40sMPSh\nonXJkc3eNFnDSWezjCCOaC7uEkl2i3g1tYgOJvximpKTZMJqu9q6tx2bizyl\nG8qownA7RNGd1TGxIkJ91FZ5amWzd7l8K/1V40LJTuih56SACFS2SpxmbdFE\nq1vrJqDdwAA9LasCsz9A9RVioxz5o42SJNaFgdKPidIdUlhk0myf4Q9wc7Ci\no8mig8zAoyGw0vslg6Np0B+Q2DofyDXQ6YOcuV0lkgrl8oEQIBNPzsrDcfwH\nNHfw\r\n=2wqj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEYA5RtZxn2twhf1Z1zcd9cA4QzfE0SL0nSJw76ZY9+rAiBIlLAX2D/HEmARDZP5gqbo5l2lY7d0ew75DA+9vgf4BQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl1fczf8.0_1613082003700_0.48432188902378037"},"_hasShrinkwrap":false},"1.0.0-canary.kl1gvy67.0":{"name":"plumier","version":"1.0.0-canary.kl1gvy67.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl1gvy67.0+168d564","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"168d56400e9c0dfa08f2ba97fb9d7c75fe8bae00","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kl1gvy67.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-SwmDn3lrJoWq61Xx/frWjM2zXFD6wPtA56TTfcMwy6aZBsiJeV8TSvLD7GhK5jWhusJVcV7Pj6W/mlW6zhPwXw==","shasum":"69c083f3d0302d2c309c73358de2b6a8ee16a619","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl1gvy67.0.tgz","fileCount":12,"unpackedSize":24004,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJbeUCRA9TVsSAnZWagAAwbYP/RuC+kJud0KuyDKhbJF3\n7LVT0xCP/ldPHrZQ/qITQ2bktvaNpybeBXdosTgRo04TfzMcOSj9MLPAvYkm\ngYI08kJKVNz9wbfy55r3QzD7Kk9+g7mXrGSItpeHzkx2ZpERH3SnUtwZeMl2\nshVe4FO3/VhS6vOgrZ/aG14sZ+VsnZQ4lPkz4VwWMBPs1ykDHmd3O/u1w5Ds\nHrZmBTPTHl6rmFfSc0AjYd0rbI/9YPGwKmt84YG6lMzVrQ4T9eiSOWlUKrGx\nxnCqQlTPqzYwL2WeURtus6Y3ajXPzV3GE705Pq0VtDyGJfKvH8btVKXjyytw\n09+arImr8/GcbpuQ7i7aObfx0rVKDvJphxqI1sCSOxYo9M57qDQJDFmA5WvK\no5RWV1cx0yaQkA62bXOX1q09s4zhntUqWNiNhHw0fO3rTodHWXTf/enKCS3M\nCHWcsQH7Xgkxn1xJA5Dqb6n9iczNxncwL5PAN4PsGCgXP41QZSom5dHY/xYI\nqRhK7nV0+sKfR1Bd2I/LalxRMckK8RSJxzO1wmHrSWpVFz2Ij+7rP5s11SNG\nZJtZk2NDeqjvXkQKce7WF0IPfpR+Q9JhNWmMl9C9c30SrUoR8rRzqtp04JA5\nGIKP4A4XuIHRv0aWx7Yaj8Zbi0bFyKe8J0Va6rJXDIwN9sFBc3TPfOvdE8Ww\nLI1i\r\n=IGtN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCtyn9PYACUNTTYlRilPZLahuEPvI2Q4yRzxcP0gWrPzgIhAL47up3FP+nwMBCxgNSJaJiunB3r1Z+VfAPq97ai2qUT"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl1gvy67.0_1613084564108_0.2832259927685974"},"_hasShrinkwrap":false},"1.0.0-canary.kl1h5unf.0":{"name":"plumier","version":"1.0.0-canary.kl1h5unf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json"},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl1h5unf.0+1e8f001","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1e8f0018682b2c1635149ba1f15148a20cdd343f","readme":"ERROR: No README data found!","_id":"plumier@1.0.0-canary.kl1h5unf.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-+GhKeULgqiYftBM1KIDA6MLNWVDdGtHVt51s2tvFXfGjE0CnwF8zjrqEOqri5uqSgMULL4RfZHswpC0JYHeo7w==","shasum":"b1f5080c1f0d3c5d6de1f4344bc7173355aecc93","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl1h5unf.0.tgz","fileCount":12,"unpackedSize":24004,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJblhCRA9TVsSAnZWagAA/soP/0/1TlCTUFNCbQdKafNB\npVUEJmLGMztaZg/Z3Sz06a2GQlzN/6AZcA58c4eHOJmVlp6RrRKAyjL0+z8J\nsxvXiVn5usXFK/7YHt6sV5WgndLMZdU700uw99HrTx/XfY4T3EJ3SNtHKaKs\nNzIc6MBO1jH6jDXBuYOGJ7WnvE2EJEvItBKmMWkovGNZEWovwpFW2eDcbfsI\n4D+EVhwXisfrx5vCeCWRJVEbjyJcO9l7gg133YCNNAbivV6VOWt0wahNNyKu\niTWLbp1NdkU97AlxOQv3yaX9S05F4/Idth03R7kD3s07/H98480IWlRGp2eJ\nVoxthWnQsRxFAliArI4kPOYhlZXWlaY3KeKSSdNZ6ILrRxFwbCOynBgGeiAu\nZgPJpjb+oX/dV1/jHzPfl1FZMtmudg0h0M7dO21Ox9sSLOEg9I5lc1aZcxDm\nCTPPxlHicP7NcMdzNKQ4UlE1xlZdLI0mtvOdeaZR6nfZFY5PxkVrw9dwaGmB\npqkb7hSYUVNQ62hGEBT+bk/Qpv2HfJVQn/YTMJl3zvgTWj2IC+svdob4ZYbj\n/sVklBGC0hKfA/naiov6NLq/POWOPq+OIA5vE8Zn63i7Al6GaSdOGei/Z7WI\n8sSrSWaKGG6PGmRYaSATYf77mc/+95+zFJxSQm2F2GMJPkzVF/eugtvQwtL7\nrpDZ\r\n=S6qN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGQet2OgvcLJzkJpsucG0QeghBCIWqZs4+p+XD3xXMN3AiAerwJFR1DSOVr0ypQnxBW8Vn2zvxXWYRlU3UtHN57StQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl1h5unf.0_1613085025114_0.6313928979769279"},"_hasShrinkwrap":false},"1.0.0-canary.kl1inky0.0":{"name":"plumier","version":"1.0.0-canary.kl1inky0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl1inky0.0+b39ad51","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b39ad5106113579ca51a6ede8e540b4fbe247924","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl1inky0.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-1Lm5TCQipT9uMb9gxKB+zLs36tCm8axPBSx5EdaA0khKGXxZDOPe95Earphn8zWlymgtXqLr0NjGEmugZy+x1g==","shasum":"8b4cb2954d09d898df2a13bd45f667655c5a1f7b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl1inky0.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJcMsCRA9TVsSAnZWagAAzCoP/Ru47kuR4CMI+vsCbOxA\ngWzoZdSDuhaKUTHoapd6Ty9URV878zxsW0IdEzolgI5C9pxSnq12b8hyrjtX\ncRDUT7KFIqfusnMwyoECNQLJmjeolFwLdjzUypEEO4OqObMbpV6J50z0niVm\nBkgjrTEZcx1bF7yS7r7gFTOB1uTHi7/0zfOng5Bi7+2/q1Yq3Mq5KEkeU5Ml\nnh7gv4cD/FsatBStFIGbiDN2hiUObecmdhNFzo42AdaQtikLNJGG98Lr9pRr\nIGCCasCpb1xFz4cEa5yG92Udmuij5oKGzgHq1kgVpDN6M7IxA/3d0JzIIq1r\nLo/dBz5qLAZj7vkdbJOWI0xTSa4OfkbrN6m2QACvRqKL+jEovzEXoB9vxZaa\nmU/zs/S6FQI/6Y2rX0snVvnt0UegEBClBJn4sYNC9jOUWOH6fWhLLQG6gNIX\n/mtn/mETls7mvtnEGwYw5W/mJxhfJ6apBVlxb/uCZzioQxUc066G1JZjdM1e\ndXJOu1jEU8I3JM8S1o0yT8s6LwX2HxJIhmA/PPLqyadGegfMnUxRKsXqCXTl\nUSf+J4ygJd05sPjOx47pRJUPkPmad+twrlVd6b4TYHaUtFKiZ6vazbas27lx\nu9lt0FzfleSY9eEjynJqnjyNpuNbxlvI3+6lbCUZ8yLE59OKJO+pmzirQgqs\nhwI0\r\n=k92V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEumBSVig4QqBdF0dsbUFjU9YGfOBFdvPqBJdTt7oiCKAiEApG/HEDUjfKWdUZkvGXZWhg30bDVFFAwUOWhlr4xT2PQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl1inky0.0_1613087532383_0.9831928854098562"},"_hasShrinkwrap":false},"1.0.0-canary.kl1j3znx.0":{"name":"plumier","version":"1.0.0-canary.kl1j3znx.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl1j3znx.0+7fddf6a","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7fddf6a198842bc07a21c66d8593d24d39cc8611","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl1j3znx.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-F+219Xhpf8u/m0s3NzZgjVjMWDIzrUjPv8d8jo9jO6imCYKFm9lEjagP8WEGRSgHBmu5D6XCOI6uwVCEQoNFeA==","shasum":"7aed636a5d0661009bbd1b297d4fdd2946bf2c7d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl1j3znx.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJcYpCRA9TVsSAnZWagAAf1gQAIJjIGzbhD49u4f4aweZ\njMVz4f9bknfJLv1sElgoiHYSUJf2Zd4gu92AuOoc0VT5N9oZLXM8lZM42hxK\nHvMMcZqma7hHHrrCAMwzWjYCZRvwMGjg/84Q6o2IgDKpSX7Nrpep2lRkEywx\nhXz6wm6+eEEE6S+Iapaqj6rqPbEoFpCii0OwOj5EdKecQ0docB0HFtATlkXC\nJrkJtra4N0zAk7WCvadRwLLBWPDdkfk3cBcj1j1PlCLBJVfosnJFcX3zTZgP\nUaXD+3a/Le64N4kpk4GNjs37H4c/5dDnsc3gFpCRg1nmtKfFDfs3NxYFBrZJ\n2YnIymcpK/P1cg0l2bFvnIEc6ifoVLrIfOQvmNyhFCnRiTnENAX5S7U1rkCD\nJNtIcuKNXXABv17FFrtJSAuXSwhVyFF1/7IzPxw5jCtbJzSj9T67Z9s5EG3/\nBgI28R7Zi+FEds2D3MdHXZTtcZMKW1C0SdAmvp0zUL6MiqKAhuyU2GdMXJo2\nJRmSuEQDy10Lrnx3wCQoMaNuQ8tma2xa3utILeOE+O8nSX76qqBv5pt3AAqq\nDpbb5KFgy62dsOTS/pUm877vDIWFnTGu1vDefZecLfcUvwZ5s9OvaUUt+6d6\nyx/6q20tAvbgAvkWdYpFWBXmeaI9UnmEat31rtAZr4KDRrNtIj8Jdq0oNfR1\nWrxB\r\n=TI9w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDkxoD1QlENkUUdtg1bMm7AY6XF7jZYnp8ntrbXajea4AiEA2Hnf9vMQdksNiapdwDEgWhcjUn9hkkL8HHqwyaXqzxo="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl1j3znx.0_1613088297054_0.8802106110324346"},"_hasShrinkwrap":false},"1.0.0-canary.kl4e1a3i.0":{"name":"plumier","version":"1.0.0-canary.kl4e1a3i.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl4e1a3i.0+327d97b","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"327d97b6b2803555df1895786434c8b51181cf1f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl4e1a3i.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-8pYTyfdSDoVuMskzHt0nLxIMv6gC6AiYeH7hJPTmza/ZZ5A4uYCmIxtg1c/rW/4MQmm1OArUV3TkBWr85Z3D+w==","shasum":"c3a62facd0052bec871dc27d13f5c8341494fece","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl4e1a3i.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgKGlxCRA9TVsSAnZWagAAv5IP/RRkYeb4eZKOgHmOqBnE\nI1bA8b+IJR9KtLXcl8xU65rZWMC0UYNo4JbxYM2PYOBhOj3JC4aZabJKY1jb\njyLW/hOsnycCa33lzjAU1/cO+prDCMOk8lPUcm4OyNi8eoGk8u6FF9Q1lQla\n566KewnDdVLD2vNN2yuveoW3Y38CEZCHjPdHvKl4GCOc5aJNZTXH4cO82kXb\nzMMKDY048V5rMIbB+xMLdmBE8R1BIig7XDGSkSImSNqUVsp7MAV1p7K2Dw/x\nS5ojTbJ80WVmsBr53HikzFGFD3WinsIyKK7EfNNdqFuMECrcRs0TpTcUYHdE\nl/f9ltpgX5VMOXLsYa08n4GTnR9NaL9gEmFdPP4R+vFDQPcULZpWR1udut3X\nnnLn9jhWtz8fGYvBDJquRsmabs7vAT5ogOCMg6lKk2DERuCruUuaX8nAGgCs\nkRze5lpRtfIrpMyIfF5ooaIk6Xl8jZSOx/J63ttRqu8Fn6qGsG85NDffUsCP\npnHo4o6j+Rraj/RhyCZ/3wn7ghDaRgwLwpIAR5C+VhqFTNJny1Y/OncITJEy\nra9Iag4LexVggFPpFYmhyy8tSqnpGuDzrfGGmOGennigqe+lcVpdVydnYx8G\nzSGNMamAcBZjSXK14QlXKCXiD7N5CHD/TVehtfODyhdPps1Glxb6QMtGfVgL\nMo9j\r\n=AqCS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB+JdhN1m4qUUReIwhE8xH3SLOG7QfSfrcApWdUDsMVnAiEAq09hlCIwEtnfZZ4rA+ar0YcYuX1uPkhxnjQlbrADjqU="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl4e1a3i.0_1613261169254_0.9004489103536715"},"_hasShrinkwrap":false},"1.0.0-rc.7":{"name":"plumier","version":"1.0.0-rc.7","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.7","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"39b0e7074752720db7d4f94355f468effe6fff19","_id":"plumier@1.0.0-rc.7","_nodeVersion":"10.23.1","_npmVersion":"lerna/3.22.1/node@v10.23.1+x64 (darwin)","dist":{"integrity":"sha512-W6OqabQFgrOctkRvn/+WZh3+WACyvLIVg7LY+/aqv+e1W3AC/7EYhAqtHXpplaKJEm9gz31RpfHHNgRs5dqwMw==","shasum":"979ba89344b0e38fe2bbe4151e2c71f3a7d79c5a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.7.tgz","fileCount":13,"unpackedSize":25549,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgKG89CRA9TVsSAnZWagAAQCwP/2o+javIdhgJFYHf3fTn\nA4E/FegJ2V2wvZqcTBux4thii8aw/sm9JDPeqJGcx0h3+xA2JGmeDrFO7RBd\n417dn5Xke6F2n2yO4xBUOxF5ubrIWq+H0BF9/fK8uAlqXiXQF1ir4E0tmTnX\n47+HbHlFV3dhuojWpUm/ofNQ0NIdzUzJRtNZsWOVWTvwJiREUVBPr33CWKaA\nKtipOZa3cuJdUw61a3+RzIxH3PL8BCeN2Jy2YiBKlZUd5VqMXykEYSiAy0Zw\nRzIDTCJAlAdHwpS/mB9nl6kKNRnpWV0foDV3p0P2iNkGKN8BWbafgzRUpctD\nz5Z28M/59iKz+fuoxc+YZEYaAR/Z4ZztbfDKZFIOuJFDyZnPQkS5npNPQmCb\n2t6qAdB1Q+bQDRbA4NWIFOQTvbBlHNm7hoLs2q2E4Q2V5Q+uZzDMa0nW59Rz\nB9wgtdC2c2gkOql6jKyrMjnJleiul67xZlRhkXNFhNX0e9Q3h3ufwvQWmD8v\nIqTD0ETUXK1hMPf8rBwZeuwlCC52P6zzewYFdZLxAuuOqLEQfwSmm0kgY2eb\nnzfXEsufSYlLON+nGalkH9+gL8bv45wjNIJsbIi6nswmim8jnsHKhF37u70l\nGwL410uBJCKZRgdUX6PXENIwia+2tFFMHsjnP88BUsVDogfNTPrrvjTXC1yq\n8gLz\r\n=uUl7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD/dFboqgWBihHdltiGrsiUKP7YZV9P1QYg6x6AbanUoQIhANCNsc/jHmNSMwyZNycTFTyeVV3qABA+JfGpjhvBo4td"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.7_1613262653299_0.4469491650818096"},"_hasShrinkwrap":false},"1.0.0-canary.kl4f3kpu.0":{"name":"plumier","version":"1.0.0-canary.kl4f3kpu.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl4f3kpu.0+39b0e70","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"39b0e7074752720db7d4f94355f468effe6fff19","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl4f3kpu.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-R/O5YSQjmsLYEoXe6KkwUOhjtzagaSgQmWccvyQPiPJMSXvdaMl85E1cTxMMHV/ORjD/MWNljEK94cxX128PQQ==","shasum":"cd3e42fc74ba178ef26f6265992701dd4d0296f2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl4f3kpu.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgKHBuCRA9TVsSAnZWagAAMMkP+wf6a6/+nFcoFSWeaOO6\nG4tGSYhLBwKiaPeRU867X+uNRAl6KeSGFGlybAXbx5P8DxRsmNkEl1aypAAJ\nh2TuzCa2hiqt3WXDyBZSfbY2mDNR3VATQXvtno4g9/nUeWVVJJr+bCp3WYfz\nf17fAWFD4lTlyEsLJ4/MkB/uoPaiPL64cC+Ig7iUQrWGraU8cK7rMvzT4U5V\nogCDPUCnuByNQF7jCQBKkuVENi3KwRBsW07c/5ZrRRx9lWDSzDOiPjWVuxZg\nDCKxAMWhcAy2ZrgCnAseH2DpyN8nL8cqvKLz/FskGTpiZuysN71mu97BzH3P\nVkuul7msVj45Ml3oMeT8TQ0+c/Y8ekVlA2RmSFbE40Ui3UzNtCeaC1nEXcML\nqmy2ibAyO8q0kJjVjoX7l3YnpUv7HTpRINy6eHv30+Q6CDEmw+eK54iZfUg0\nmwZE7lBBlcLm7P1v5pw4bxAcPnCh/XPE1yfKk0oplf0TPsrljUouJ8lGZJrD\nRnOhe5pnHEhknKok2rKah7PTWzIOvq5b09rhEThUc1+VGsDUNKaumYSxYSoB\nBbuq/cg+VLOTZWmmVjankCJty6ynpMGj9BiEuupzzkeNmD+QOh0gR5Xby/+6\nFcKmEN9jrmP35VWwuYeENZKK9h60p9qVBbkkSX63ijRIAY6tqGHQDoVv25JS\nXSTJ\r\n=jrxn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCILbiWWy4FCWZxUARvn2thT8SLnK6X/QrsZ8yZRomk3wIgb6IaA7VDmZvbg+fqQMGlOiWDQ57PAKZO2+MgDEvgDLY="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl4f3kpu.0_1613262957642_0.1753856084230816"},"_hasShrinkwrap":false},"1.0.0-canary.kl4fa8dq.0":{"name":"plumier","version":"1.0.0-canary.kl4fa8dq.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl4fa8dq.0+4b5c23a","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4b5c23ae3eebf51835b3eb9fadf266e041c16a9b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl4fa8dq.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-5zMmvrekar7Len0X2oDhntMhqLXs3PDAl7RtAnFseF5Ct+buK8Fg2aBtsgkiJ2GokwxTqvkie0Wx1DCnA8yL6A==","shasum":"a798dc9dd2ba2573305e0edb2edbdcde1c2bc41a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl4fa8dq.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgKHGlCRA9TVsSAnZWagAAIh0P/Al29xQnpEMzL4IuLhrC\nbp4CLWCiPVZ/c5+KeKHhzaPjfrG8S7ewBHgF9koiig+5XYe4v2eOMO4s+9Q7\n+ZyFddUAeVo9TC9FHxPMv9CIrcnwkrr6wQ8Gb5gDKQrhnVbvTGz4A9UQJ3hM\nFEuD6lpHKOLzbdzn6wcO+NoQ8cVmsU7RsO394FY3nsCRRmqIfZL6KpF8U9sK\n6BJB4u1yiFNYHVGMN1hD10afhCEe6qMMGEOZfDOOgBjhkN7SrclpaCzS08R1\nDDhmazxre+DErdX4mJmv+mnKaBcmBL0e17xXO8CmFvMQohMfyc9Jc/uFJdOO\nsqJ1WPHYbY/v51Ylfz0Ayn+L4FCpOmh+rdQBoYNKnyiCBDuuDbs8m3H/EoAo\nrusVnxHA+pOINNvHJ/KRgYzfrI6g/jA/nhyGdSoRWfwP3RikrY0YGxaomZRf\nbO/syHp+Scjl/XwfoyMDI7IWJpl2OvFr7Maq2vmNspNtiybuHGb2tAMQQk0h\n/nKvmaf8r+Wtj4lnEOCs1Xh88wJ+5YPIRX1RhI9P4y9oGs0da9c5yQy8zHB4\ncJYAp9LiMfRLfdztqprj4bSBXhYyuhpTXHLQREtAljhL1ilM0MC5T8tIn876\nrDkzeZIzpRhKA62Ox6zq1h+rf/2Qlc7DzbIopzJ5sTgci/V3lASbUJXD2ouI\niK2M\r\n=JgQx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKH4cXV2yy4b2qOUda1dQ0ujTURwbu3FLztdGFwP3YXgIgPbnlutGKiMoV/H6D6Sqhx2R30DwAawu3ZzM5LR8vokU="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl4fa8dq.0_1613263269145_0.06971173770931927"},"_hasShrinkwrap":false},"1.0.0-canary.kl7kgjgn.0":{"name":"plumier","version":"1.0.0-canary.kl7kgjgn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl7kgjgn.0+ee4857b","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ee4857b50aa350767e709571e72ea8bd44f7d475","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl7kgjgn.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-9eXjw7HjBUUyREg2UwZqQanwsUNI+JZbqC++/7csy/YlAGb/kPovdd7XT1QMEkWPRF2xMOhsGGTb47wyxfpsiw==","shasum":"b1a3ec67028bed2f45b684deac26be71e146637b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl7kgjgn.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgK1guCRA9TVsSAnZWagAA7KcP/jxP4FPsf3oTHPg0Vcrq\nS0fdh1WarbJq97yZoG4l2mUBIvOxiQ6nxUdW5wtDMW0JkI9xBf9lPZCmZzNe\n3QJQ/aOvIjL17OPmEqGbip7Te6QN80GvgFMHmTL7qkYr67KpjWZcWu2jdsLU\nQE0vXFGSRldQ3C/3h22hyxUYcfmIn8GJVo0227/PIAhg9wcnvlOLhAFIox4F\nzR06czem8GbOG6fj9m28aWS27TnUhpz3/uZz+L7xPdqvdcyzpJ7NlwybOgH9\n+2YHZXqQoS/9UzvJ3292b1LkpKJ3Of2gyG8Joou9DTp118GVjzDg7RDDJddO\njmDjr+4Ggxfn2kb9Lc4g+wudQh6+Zt/LBND55n4rzu3vUaHkAVyNyfAdI1Nw\nsjQtDnplAvWgu+iTanJfokkwzDXL3FGf2/ZCHwKl5gIOaU12Fml55zJhbgQu\nBEIpICVW5AQ3VKJ4dnEQWU1QlU1V5B5YGtZIDANT07OTrHezc9t3ZRhMdtgR\nJhZZK0wvBT+qnn/u7+tFaXPtRcs82TkdI94Wi6FAAy05MLBvz5ribIZEQdVg\nJHL02Y0baqAXAzWLKl6G0ZlRHrcEujbuRRyB6OxmrvY6/wo3lsmYVrTkT+9a\nkqztovaInsZJQ0Yfq6g5+vekpXgySGLpB+LolWr5aqfcOf+xfDC+lXEBvX3B\nGotO\r\n=FVIh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDE409rAm4A44HC0lHTkWScw3BME+b8+wmhZ/g2B2kgegIhAPPy4JpVpoc0xZdEBpNGhM+xkxqSXoysxZIEWqwHf/T3"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl7kgjgn.0_1613453357917_0.2709012550091303"},"_hasShrinkwrap":false},"1.0.0-canary.kl7kqymo.0":{"name":"plumier","version":"1.0.0-canary.kl7kqymo.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl7kqymo.0+696a69d","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"696a69d3a2c457a5baee89a11e6752da61c02f18","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl7kqymo.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-YywGjZo0vkQ53HyRw1gKjIgK89+Xer3hDXmbQmb3npU1IfIn16bygUIAQL+e2xGV2CT1SHsOFUcJQ1BoPZYQag==","shasum":"4567edb3a2df7dfec044e87a554de1daf660a0c9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl7kqymo.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgK1oVCRA9TVsSAnZWagAA2bMQAJAUS623lnInyc4c195l\nfG2AuEWl9arzaTQIH4gBcU/R/7fAvG4uz1ct8+FsAjRkBSSEW833TqsNDyir\ndfbqQqJtnyU2SwM4VPULjEDcOcZaVRcybUrp1oOs6v56CWhy3L1cTezbl1KD\nbW/qNsZl1tftrJQCBmk3uoAqDtH/WmKTf0kiKv2WAnlT+PqOX/8qfBPMP7SV\nrrSOO1i6QxlfB1F7cK472LPdOCZWOCPZoUQ4G2+uAT4LQeqzUpEX3wjZg7pd\nLIdC9wOnsPRFE/B44JVTbT1n16Bi7nVdcEIVhd4wbsubIU1tolX5hEZGvHF4\n9NPn51fRshhF0fe8HhZ62RnzhEeDIlwXYGu7scnaEZf+lrwDco52r4sCmWBR\nbPlJMI4tiydtaIwhtB5v1uSzX0tO1T6ts4Khe3TfK1kJgY0AtDsiveDLKrhw\ni+YugmRTISWRy7m0qs4W4Y5ADCTA5AZrt5NXmmBy8HBEjIcmTS6GIw6Yo1QA\nYXje1Ev8sSV8gWVOPGNdCPbp5aVPiJ9IF8aaxi4QvvqYKgR5JwkCPl3dU233\nG0q5y7tEi0Wws6WY+PlpxDzjddxjNBufpI1Fm31JN5KPV6O7wzkPVZzyApki\nuNIkt48z70SNHPdPogYx+ZHtTdV/0EcqZV/jwXx9X5ymYT7c2jJxf2UFp+dM\nLDjO\r\n=7BmL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC9slA/GPJt6rOhxIPayZSltCnBXvF/rZMtfxPP1op+HAiEA/T0Wt5zb87pxVlgjtZhqm+0OmRTJ4N6QRxdgWM5rlRE="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl7kqymo.0_1613453845446_0.2214039130991432"},"_hasShrinkwrap":false},"1.0.0-canary.kl7ksxtq.0":{"name":"plumier","version":"1.0.0-canary.kl7ksxtq.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl7ksxtq.0+51f3107","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"51f310753679e96986250e1b20cdb6e01c2fc192","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl7ksxtq.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-LFxZ9FzAe+ZDjEU2dhAehsvW8f07esQtn2LmkTZAruLJw5pL60fDuft4ZgFIy4f/i6yeGRqVuLNch+ILYuEftQ==","shasum":"1b4c8bb70a186ddcb9142d6d3405557e7add994e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl7ksxtq.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgK1pyCRA9TVsSAnZWagAAQfQP/3gYc9cb9DHhNdQhfJR1\n4DDWhXUdVSPzs3h7+P+P8bO9KIfhghBV5ukatnNsd3sxuZWNYTa7WrMkzaN8\nRemJhMqK3u6g/+eyRaNsnd1UL6HzDMHvkYFIzIkmDehVVFoONk9PgJ9i8dOz\nfH87pyrpcDsOPmarebWiq6uwMjucKT1b9Mn9glv02H8C7v9OvhaH0A+WeoYj\n3htAIiY1BnS5p2oq7L4DzWY+xapMYuM3ky3rRBUJUqMdAzCR3iT25nNz2KgK\nK+lg6xjUh1C3uGZeS4Ot1RjdZWx+IDvuhbKDyKv6sYAtC+V56tIrD2DNLDLe\n01rFNPzkZ+FrebJJ/ONg6r7BmcY8NhJ7o/i9s1Pndt1Vvt4BvPMcDv0DF1On\npzLMxXhuOfmOd0bjHQVb8K7lEg71zjLmqGneUBvQbH5EqJ37AAeWIYkOmhT4\nta7rpnJc+a2TI6qHHtczEBdZ7wU3kiBfnTCW/G7AMrKw9+nitz9cM318Pat4\nHxgan71cJERcUydax9J0OlEatFrNNxXoUQtOTu4lDsFfu6OOX9ifSxWVVS1M\npcpPT1Y7DU4yv/sgiwXM7a+d/47rAoeZqaHUI5ktVBVxOU6eNHNOEbxk1SkD\nXQlrGQFYdXX+Wyxpx+coWijxQXRuFfWocYkTiHbA3ZPIq/ZDyTe1ic4mjW58\ni686\r\n=FwR8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpcA0ggbqZesAwCZMibYPb+xu7xNnMK7CXeIilOHV9OwIgdnFnhN2aNnaOz2KBqG0gZBJZhil5uaWurzk8fEywYSs="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl7ksxtq.0_1613453937870_0.5760515569870905"},"_hasShrinkwrap":false},"1.0.0-canary.kl7l7kcb.0":{"name":"plumier","version":"1.0.0-canary.kl7l7kcb.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl7l7kcb.0+ac65b7f","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ac65b7f29b8a28fb2722a4769c04fe74220a778b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl7l7kcb.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-FoTZmX2S+oeTFOIycvVHX7VSrAxIV9uf/O0BMUmUW74QCdBaga3pl02sQ9dhEW+Xt/ujFfuzZPDQ1xE9tr/y4g==","shasum":"b2b77a419fd76142608e9bf03fcc21a4be99c079","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl7l7kcb.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgK10fCRA9TVsSAnZWagAAHLUP/jwcAxWkIfLvktxyQiUk\nzHteCgAPvkDyF8E5P5o3G2QpCb3+4pYK3wuKUbUUwe9X9hP4VazBzOWUs9BY\nrAK82xpkJUsfmB/Xeg8ByPb1qvylZPP6d8cZWr8m8h6tqmeBwulWaQTg3qxF\nNkQbsX0JZawvr8renBch9fTWt+PoRRybmWxy9eAUbXQNkuNI3O7QZiE6r/Da\nWY488COtwJbETKUAA+K9CQeLKWhhvKw0Xds+QkDGD5CE5gtmm2PtDEfGk/N8\nQtcBg0Xohwn4VgZhDSpHPa41x/NXmLj2qRP43dWrn8pZkEkdMWH3syxfMJqn\nrp0YbEc5PATSnL8cGwLsT0nPQqNQZM0I3odvtdcGWLtI1/Y0A7kBFCwguPq+\nlKleAZ9HriloqZoYJ9ZotxyOUSjJsOsGsJzY3UTnyOIKE1L8eZD8maHX7eGH\n4NHQx8vqKqBmL341xG+13oLvb28Ja/w8x1BKC53hfvyURi1EHh/zASi/jhsw\nMGkzpUV/K298870WtnERFK/f16XQgmh4WeTJQjZUm9Ytkatp1njjWpzg3+1g\nyoMLE10gNUaIHBy1S0eeTsPvOZlYbd7VJO83qWYndUIP73IN1+Mv9rKhekga\n24KhhzsY/9hyzl7Erp5yze9I2Yl6xlIXBerG1ni4ls6fzBQe9pJLlctNfMuJ\n0Yfe\r\n=Vllz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBE9zGsH1XXlXF65+cnW/y9CSrfK+4GvuqUfMA/Q9IxQIhAPwim2rVC10rN67p4L5ACgusK3KLZ1Y4+S6V4RizNlNM"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl7l7kcb.0_1613454622805_0.4293816004911384"},"_hasShrinkwrap":false},"1.0.0-canary.kl7lb6y7.0":{"name":"plumier","version":"1.0.0-canary.kl7lb6y7.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl7lb6y7.0+7c08a78","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7c08a78b3fe3e1bc3fa1549f26b15fa9fd99f2c2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl7lb6y7.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-LMz3yBUNU7KIKPnSR8tkLL04Akh7hfipbcfXShvFjjZmliKGElUiC3+8Ttvq9mKEHpheYixsSjZKQT4ouoo3pQ==","shasum":"5f51fdc96d18b3c2bf53b2022f6c435d62998921","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl7lb6y7.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgK13FCRA9TVsSAnZWagAAfrYQAJrmR2DSfGCoJXjg5VCx\n6YaD1Q2OAdQZjhqzrpUPTKALliGIAD7tjFmfwGoiTsaV6uOxqvd6Wbg8dTrI\neHiBt7xIFeRa4BwBBby9pzoY4lgmxdnV/BMBycnim1dTUfdiJ3cE+Iv1Ff18\nU3/MrAWnHtHn0kAB3keoX+eSR7qgdDQXIhFdh22DbsuM11dOd2VWaPr87jqA\n40K6VUgrWTXPCB1vD7DJoE2dfDhFa/N/b0kL90zAHftVDjzXbQQnZHWNJp6g\n7VaDOnc27tv0jrNAjjaBgv2zmsZci6JlgFT0oYPwx/+FK0svMS9wVymlNdxa\nzDr0qd4p21GPbRvnPbGkScg9TmXmxs55YolKbTRgrsfreVBLimEn4soYP0ix\nh36d3tq3rn57e9mTc5kATWFeqMWApwsMfQ5ekdIm7XrYg5XbXT265THmic+m\ngp1vwzsK4fELjdACW6OuZMoexAHxJSYO3SqOiScxDm+1D1frveb6UH4UXNn5\nN+7TrRDwCN++n6a2dp3oGjg13MCfqH0r3BdL/BuhHbBSgwiOF1KhtyDU/pwG\nni8VmmD8Y2KvZQiTTQAKspKopb9Etes/HajybVDsH+WsH9ibvC4jrT7tMUa7\n/w+KbZbpS3PptwOmrdPDFdzsSxzn4pCD/nACFomajTlDyh7ui5ERLgFRNZs0\nJJwO\r\n=V7dL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCyPqG1rpIrOMqyMhpl2P6J8krW0IBW6AVV3X9EEtg5rwIgdIGLvMlePcfcG/Jg3eti2rwR/Szzifaz+CcjJs7/9Vc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl7lb6y7.0_1613454788727_0.4426521602859421"},"_hasShrinkwrap":false},"1.0.0-canary.kl8sdtfq.0":{"name":"plumier","version":"1.0.0-canary.kl8sdtfq.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl8sdtfq.0+e3de46e","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e3de46ea41091680a31810f8e8e7b15cdd9b4b35","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl8sdtfq.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-mULzQlscmkhGJHDH+GfBMp9255T4AXZ0V4ssmtRNVISGe/Ua3g5Jy3a7dwZ72pX48ilL5jJAUYyuCmWK4YiQ0w==","shasum":"da82035ac6ca64020d188b29f2a6be9dbb66d553","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl8sdtfq.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLHhmCRA9TVsSAnZWagAA8ugP/2uGywdXyAKPp7+7+R1j\nwb7cSh8ra5SqnZ+qlus6a+2zRou9eq3H2MorPQnNd3IWnckDn0cr3Q7Te4v8\niZDphm71roKBlc/WvhEkvQEbMQts6pxP7aA/MFWC6Shkl/QkUPTKqSXfa4tB\nCtuqvMyqHQZuXaFZEkY+qPw0uAeqOIm4uasm9vh1Dz1AdKIChEo/eVORqbCa\nYlUlZ586hmHE29dkX/N7/IPhvgfpIQmPhjLK4yU2XpeCZ1oE6o37bjKGps85\nkQATbOfuBkJi2UU1VVbTEFPmfbMVuIzJLBNPPfmcXXQbwMN1SyaiYgmICKJq\nQVqCBUbFNh6L7jNc2g/hrUZDjlJoL+IePhahj6bMW4PnJMbJaLdAbMnMC451\nTcDIBjt1ItUJxLAdVGMf36T1C7fIkwisxdss7w5QLHCUKAEzjkbVFslluf3P\nmRvZsBYT51dX2HxaZH0nCmrV3QHAm58nA8abPMn2zd2tQ+7oAWiunSwt2bJy\nhCFV3kK2KX561FJOhKXy1LNkY13z2NoQ+oSNp+fjcTk6O1/dSmmmp8EfuPjl\nTPbQrZWdtj51hoZmQZ69v2MGTuBzIkcpjX50C4L+BqL4SuBjCnMWW/hBtwk2\nMIgP10IzINm63JahH7NKncGjsh6V9S6HAkWH1jJYLFii2mitOGlBGRksKbnt\n9JjK\r\n=oi3r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID5n/KNyx7w4+otFwn0e46PesWbex7lZRVSxCSwb4Y57AiBJ/MI9BCukOUYmyy1EUWUgZ+H5oMgjZoGWD+AfZKkgjg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl8sdtfq.0_1613527141853_0.14734051001618287"},"_hasShrinkwrap":false},"1.0.0-canary.kl8stpwu.0":{"name":"plumier","version":"1.0.0-canary.kl8stpwu.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl8stpwu.0+1a3b532","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1a3b532f9c9ddf581497ad8d2541236653bb2c37","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl8stpwu.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-t59M3VxpSKAlmetvzHmpNLKkezdyw9Z+rjwZ+AQbAGAK3y37nyIfFjP5syMypBAn2qoyhGL+nuE4PC0cNzE+BQ==","shasum":"22037e58aa1778782047283736e8ecb06e0f7a6e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl8stpwu.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLHtFCRA9TVsSAnZWagAAvT8P/A5wuDv6lDawnJcTOaRL\nF9JyP7So/oaD9wfSanZUXzdNOjY/JsBqYftWB9JTDgMKbPVtnaLwyGRDKCzG\n/mI8zPAdFy5t9vcLgQUWfF6plXuNfeBDuciDQjD9OmlWF0wJ5WsnF161gLO5\nuE3dNHdGl1BsL4if/eaYuLelSnVkDc/nqNdZJZ8gZTLyZgE7YL7HHdfmdR/Z\ndqg9Jntm1JNGjroTP1Gx38Vk7oMpwy3ngXluVk+p+Gv4WSNkg3j5lSS5c5cA\nGLhG05PGUtJhhXOI6y9+TPbrbZPWRWh+ni0UYI5blcFpkUippYpiUw1S8pZf\nrhWWGkyGRW3h9zIKmnum678CrI1R9fhN5oA4XEeltq7PziL6c6C+JzAnN88O\nqBQ/5lfqP2PV0p/2fVM8m9ce7MFmR/kd1rkE6qaOn6Iq4uw/4Rdd5Ho0u97H\nMXZQMz774achnZoL9JMS1BL9aZw8a3MRmR7iGrs995YG5FcwyMWSgD+ow7ME\nbe6U+lwmhmskMoYUmBRMNvZEroIEB5uoArskILl+ET8sM/+2vw5XbMLIQq8v\nV0hF8E2tsKHe/Yp7+KbIMIZcjTepHR91yEb3ztMDN1tlQN62nex8M60SKHj/\nzm0z8dSvtUZsWCLcGnvVQY8Pj6gDvtvGn6e/FdxcjHTcQiBa3ykPQYp9oYC8\n6Ujt\r\n=LuHf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCl1yojz/o/mj/qNe4wQafTl/5CbBrBKiymcTvL/gU+7AIgYgzpfV3p8130M4dKqUNg0sGfyt+NbEZbr5+hap2R0A8="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl8stpwu.0_1613527876902_0.12757685616110792"},"_hasShrinkwrap":false},"1.0.0-canary.kl8t06nx.0":{"name":"plumier","version":"1.0.0-canary.kl8t06nx.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl8t06nx.0+179bc5d","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"179bc5d93ba0ade4db5bb3ad8e938613247fada0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl8t06nx.0","_nodeVersion":"12.20.1","_npmVersion":"lerna/3.22.1/node@v12.20.1+x64 (linux)","dist":{"integrity":"sha512-i5ZY3Ik8EdpUSN1SmmT+xbmtzGy6ZRLFJjGTrbWTL+kUxkVf86t1Gz/e6pKkZrL213qqoAOuQC7LGY0eqMWXdQ==","shasum":"a9b85920c9fb20cd7b3fb3906016a71007ed3f80","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl8t06nx.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLHyCCRA9TVsSAnZWagAAUEkQAIUxuxszYKU2VJH7C5EW\nDwz1eIMLonOjLI0DjTqX+sl2wZdk3kIPNov7P5j668QQGgMAZ3v69SwB4epu\nNxDCuFGAjrxxLLF9+pb87J+vhs4setXY/Rf6dMX1AfAh3cbE8+MrS1nQWDPL\nx/NQtQovtjK3+0x+BjWxLw8PkLVNDLJr+vsC3mFPjQRxw1usKyQLYxbkobnz\najxbs3PvcoozAOlrIrOALeuzq+hYwg8MGEjiRVCPsJorv7+3TVjMD0hW7XzK\nekFkjcWAn53jOtbBpeI2jUrMjuJ8JlwvFABp+xcjVRoZyg3P5fc8X0ZTTn+t\nU5vaARMWntKGfqHFnnf0chh7a2GHaEw3IyIoPQz59tWW89pc/Uy2fJ1dNW29\ndSJRt6sfbXup9nPIjshUcqeksDwt18kb1D9+7DnP9JannrgOoRHWFseJDngN\nMjSmip04aglQZIJPcn37mJi1xLGzGOeOvUexRZtZBslYzQrvNR2GTQ6DlqtV\na00dyfyjYqEAe8aJl/2Z5vTKNqDFyhPK3KaGGr9qhRzqyA/ystpq1DftZ+iB\nIlW4iMc4PmB+2e4E/PKLInbtw5ZDvBhQIrxJ6G3beZRi/UeQtZEpC7fMI7J8\nnVdJ+99p8juXpoWIf5cjINPFifvvpGE53ZLgiPJMyN+vKhceFF+Ot0oZGZ4L\nskQT\r\n=cmFQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCsODOG9Tb1S8BSNCjrNKm2TMl9/8hXDsKBzKFDr/jXwwIhAJR/6W1uZiO1bD3y7fqPCE3BBadqvk9HUXbvsCPNM+if"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl8t06nx.0_1613528194071_0.21376560566663283"},"_hasShrinkwrap":false},"1.0.0-canary.kl9xn7u9.0":{"name":"plumier","version":"1.0.0-canary.kl9xn7u9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl9xn7u9.0+e60b165","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e60b165b43bdb3d1fa2986bb2c8dc500e85443de","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl9xn7u9.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-6cUr//eFNATEI7EY+2ZyMkvYIDxiT1oKqpKxaNsuAYHnV32g4CESpdy9skuGiBxX80LV4Oax+PLb6sYGvvbM8g==","shasum":"24b7458a5d40477dc7734ea2a36e1df1bba50a00","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl9xn7u9.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLYcWCRA9TVsSAnZWagAAkRYP/AyGGZiA1cPeuVXerBQf\n/yIhFcJF6uFOjyjCn4SB7oX9xUk41dpcWotjV/LW7wkRVaO49rpoEHnvw9Y1\ne0tNY2TRRghNblWS7SF7/f5lz1pGyigxvz+5KJIyzxP6ZfwX3Op1TApXHsfn\n8ZX33ziS8GXg6MGmuEKpYP4qxQYGnC7lL9Cyn7rGWvJ4nAQLqJniO1oS5PQL\n/Khp7810snZM1CVkJaKd1lRCgscQ/Q6YXBLmuZGir8kaxvyZgBktExc2uyOy\nDqe1/srJy+JWXaLritFwePzvZai7nZG+WT5dCIU/v1tw3u4uoqtZpvCtcnzo\njESEnnkWshcO0xwahnETbsBD4SX8sRBeKQ7H8SGHXxXq7dNSbRNaYqHbhD7O\n6csBu/5Xx1BfuFXRdI5UECHJ77MGiwGFdJYkEwhmocMH5WhKJfl1Reel+dHh\nf95zXabfUFC0iWdpJxxJgmoJ2FcCf04uFjfdCPD9N5xeYVs7WvNzoUVrFZmJ\nF+Q9pv+bpmWYbKj4idE2IbUQXax/1uS5/d9BzOKgs7zUmAZjj/Q6ffOe/paU\nq1CBbDbm0XkvYyJyylcHxWGymYF90eGVC7FHf382woPixR7eQDrxH1ibxVl/\nWac2PbhQ43CXPFrRtpOg0vCai44irD85fE0JW7Og7c2tO9J0sRJoaKTw07Wp\nS7Tm\r\n=cXzm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHht8AWTIbJagN+jytp/n9ajL8vfttJG0yzbsBxKYfwWAiEA+Vp/8LQDQjYnJ6xrcbgU7E5xbkV96JKg5pTuYzMSMKY="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl9xn7u9.0_1613596438465_0.05509917987125457"},"_hasShrinkwrap":false},"1.0.0-canary.kl9ymvhu.0":{"name":"plumier","version":"1.0.0-canary.kl9ymvhu.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kl9ymvhu.0+f1a165f","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f1a165f3a16e7aa3fbbd5b46e38fdcade7bd978d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kl9ymvhu.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-TCTQvj8SCV0FD7XmCOAjmHTlK4SVrEJ9FgeGM670L0ydEJn55MzYRFsmEOrmMlAJUV/iSaAd7fNXIy/yR2DAFQ==","shasum":"3be5db086f6e93dd99ac6a61289c326d67bb27ca","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kl9ymvhu.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLY2YCRA9TVsSAnZWagAAHQcP/0GPdebeEgNNOmg2aYL9\nbSR2oHVAi8Gw05llJ2s9rkSiGh2zCIvd5VjZHwsQNJufwZNfwEgsxahBV1Ou\nqZMO+Uwb8Bb3e8TVUlAPoue3iKBr5obiliMP9jPMPRWjUVRbqaRINHL6DqMG\nuLOofi/i+Y88iLB4vnH0XwE8iFD0Q+L6jmo0I5bkGAzme8+1HRFi/x/8QcPZ\ngoNhIt5Iyj/ygjbI7Rlz53NThzEEqoLapNKKQKJMHnXmycPYAaNXMODgwCCG\nGPawMIMHQmNUtn3N1+1Ttu1WZnBYykhyjeCyhpJqBHkdWJxaZWOTHmPaV+ND\nB8+oBuQWaV4C40sH2xBFcxG9nQUmvZB1QZjet/c/1e1d3mXsxRVPotQnxTJ7\no23wak5nK+P/zJDH+KSouo1PfyRmFqvP8tRm+Q+wrhkfkqrhufR1elacpfhZ\ny7hPuery0qiMAJ4g0r3B4YJozC8VgFspLx7eY6lk0ktSADJkQSHdfG+bIKyz\nNNpTc26v5Ds0XRzZ+JAgAjSweQ0EAZADnCAxrtHGENhS7sPE04YMp1vkgZ2W\n9JFoipwywIUruFn6eLMLosHGr9uTxUMEPKvpHRhM0opIGIfWHn0yC44X9qgU\nb8xQ+Q+o+EdVX7jH7gu3gQTBMwHPeRgGkZI+seV9P+S+bmrN1ZUF2DlGdWLM\nHdPE\r\n=rct0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDx/teaLiCYFjXy1r4MGPSnUHjFbdWOX197ljWfFlLG7gIgJcS0+7EHvIjLyp/Zge8Pv4g6eSqi3Ql50dRqkQ4iIwY="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kl9ymvhu.0_1613598103872_0.6151738919515453"},"_hasShrinkwrap":false},"1.0.0-canary.kla2b92n.0":{"name":"plumier","version":"1.0.0-canary.kla2b92n.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kla2b92n.0+65df2dc","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"65df2dc234f309538a621ce01a40aac929249a59","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kla2b92n.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-Nv9SepmpumhHXM+gdkPCy+nXWWUYYdt+UWF+WjHrQvtSn+yChBkLnMQJbg1qLq7+AoLp2KkywcIbMWHmagmTwg==","shasum":"aa61450e7fafb75d155ca1264e6b4b834484981b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kla2b92n.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLaW5CRA9TVsSAnZWagAAntgP/2jH7V0MOu6OHrA0d1YZ\n2mTmzsbif6AK2ar3B3rhNcpuzMtaAxBphYLK4yoONn1cPHehZGtIAaY8u15j\nk4cBDy6E6s9COEnxGtdZoE4uMbP5iLANFj415qCLef0lJd3WRpwhu+HMbSyY\nIbg443cofOdaf98yIpjJ2J/ZC6AoR156Q/iPNYvFOpNqk4ebrbWmmPOUONJI\nmcddDjKWmD6dDp2k6TBYwUUogUMaO6Z9KrO0u6hSMi4wewISCp3HIeZpU9T/\nz1mZnBUcnxK52eHd7pY53PsOVo052x5oI9i1T+eRnB03QfV2hk+zZE/MzqMM\nHO05qhFgeSEOGkxnCoX2nGGWyEDvH3ZoHUuxuXcngg3wsTfy2r7HXU7wI4E8\nRk3wAvMA6ZgTWWvLM/UOjm73xCvmN1btsQlN+jJNw+4pRXzQ5KSbFOOThsso\nqiYL/ecBBYZQDga7Y1banHDTc6uIsoKvxyoguXp76v+x42B7LJMg1dj5yp6C\nrOq6b2KGQdAOSPvKFe9KG4KoBTeqCmdHAWciT910pbmheW0dJNtRPZerYBhT\nKqRJIEx2uMIlnRhdOShC+BdGn3b+dHiidyZOkAr1lgfszYHld32nH0sOqQWd\n/GlC4Z63DMiqF4APsgSjDeHa5AWbANzqAS2q3QRH3M/Wxy5AVHOYQdUq9vrV\nZfbp\r\n=i0dP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBpRluszWD9H+fW+jsk1CG6YTC1SS9KzIMNcMqe7PW7jAiBSSrh2pQUQOnpOKxNXIRvk9KJUjexXH6eUbKkXgZV0Bw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kla2b92n.0_1613604281376_0.4332958520406658"},"_hasShrinkwrap":false},"1.0.0-canary.kla9tiov.0":{"name":"plumier","version":"1.0.0-canary.kla9tiov.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kla9tiov.0+d933a87","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d933a872818469a3ddcc782b271eb410f0a09c17","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kla9tiov.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-gPToMhn7BR79PKrYkShgBQsjfTaWTz9eNEuZ/MdXCGv+gLWE9UesLHEL8RUSB31PcQqJB/ARptTyEWe0JvRDrA==","shasum":"988796dc0e8a7d4a692b1a445832e0fc13b09fa7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kla9tiov.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLdb3CRA9TVsSAnZWagAAQKQP/R7EMIfCcvaLp9uo8xTO\nrOe8jEDpTrPdQPG06+djEMIMCwIe562dn6ndShcA9GlhNbKeGK93UgtzjDCh\nlIaSJ8TVixZ6b+YP5eu55aJZdFCS7YNsiG5ltxP6MeWvTN3CfN6WuzbFnECC\nGQtno9NWS19IePpiptnrT1mh6lv8GpUdXqEq0cp8OYgEdtpdFlvT98FY6IH1\nGb+FUH6YSYsnJ5VfK2uSLhYW53zxmWB1J/ftIBZ+YtpkdHDhMJwrq6Ng/2g/\np39VJSkxBJInl6IifKfgBU67zTeg5hpXtWFxNlkfHjLce0UODhzwIlDrM5sU\nbFHovIvEYERmIc4LqnDuZqDKHSGkZ1g2Qq2v2DZewWx8vOZfxs+7mueb83OE\ntGGsrmWRZ8aCAp6z7dAgTKsYdMH4PwRYaq8yQo1xAr+NOciVdJcE0lCg+1kH\nCoFCFJxawiptj4sMTxWXoPGDZaq/PSfLrPAkNxvwr2EGOVKKBB9Gva5ElZK2\nO9fZcWz9ydq0SZFMsgrpKeHTLprtmHqMJYWuXtUgvWlrIqhRxGNjQRjiuDE7\nJAqi72LlCIDuNfL/JBfaqdmovs6EmKpAe+vAqwoBrdC30/nRaC3BIOdtqsf0\nxL8vH6esyQ2iMHXZwKyTgjfXXRH+9g7yOKLDKgPXkRkSEr0nSmQvEO2J7pYd\n8RHD\r\n=/lDv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFjy9Syfp2kB78sghVVgtUtYGXCQnP433TqUx8KB1DM8AiBTKNnAL/k1BG1F3K5kuydhUk+X5acMnsjnlmqTir9jDw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kla9tiov.0_1613616887447_0.9283826135340874"},"_hasShrinkwrap":false},"1.0.0-canary.klajfgyt.0":{"name":"plumier","version":"1.0.0-canary.klajfgyt.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klajfgyt.0+2971e14","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2971e142715b30588aa06bbae9f9ea1d34142a29","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klajfgyt.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-qROWMQUEmCK/qxgn7VrlULcpxlb3deuavRzSJImTG6jH7/ZQCVXi/+k0GSW5acfPba4HkFvGZt49o5MkehD1ug==","shasum":"e3ffa34840cf64df9f9e2225ed7204355ac9626e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klajfgyt.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLhYFCRA9TVsSAnZWagAAJBIP/3e2tf8DtRSQEazr+5fu\nwtdVwgtqTkovEuRMJay6o4icdubcfyjUGUS8lksp37T+oxW2swVJVO/7Fqrl\nzEtCqZ329XnksbnVg9pPXt5iq9qmwF7x95UK5YN74V6i5UKPmMqJXDNjZyiY\nAPUCjO93WXkx7IEV28jQu4COGq+lCrl1sof/VJvFJEs7IX5xsdiocoaTWfY9\nVR0WBNNRErw9pbdOrQWB9RjiR/D2VIGoW+mZ6XDOp4Ynunc/pG+ARmWqvk8s\nNiPePVktZjOUkP7NL9/F1WiVYApALX4cGB0r4i0/BXLgOpLkMi850EKZQckw\nagpg1oR+d3lRvVyH9OVPOQyQhf33v0ZPlmNoRkr+iPUjOyYyvx9Iy/anKBY0\nl0G02qIL9XKKbYIs7jXfo5eLwvUoj6EYozvi+pO7s+jUerD/Dt9l0zpaJ6q4\nYvpukJwRfg9YpMJI5SvDjy5JpwAoqmrT2Fvr2r28X0VNMOpkVEeUjh+zeaEZ\nHGHwyZgEzcnPQBofBs4K51PEn/M87JZs4SLr7DkwS7SslB+JI8uh3Yey3H/k\nsCDPaPA6KM1fnVma3arf2n/fiPglWRA8aJX/DW5HEgxdpjV3wiiQTTpMRRpG\nX8/XBHWUnT+5T+zPRd9IjYGvtYbXvzK6C03pnBUcl2A4gF3BqPvI5vvUjK1Y\nzyjH\r\n=hSk0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFUJrCEXeC50OtGxkVipsL9NP4rxZQTd6s5cbS9llixRAiB6ESfEKeeNnsIk8Ew0W7fUtfPO0zgSNUNQWrJ1rV/rtQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klajfgyt.0_1613633029139_0.6895196064677909"},"_hasShrinkwrap":false},"1.0.0-canary.klbfry2t.0":{"name":"plumier","version":"1.0.0-canary.klbfry2t.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klbfry2t.0+5848d10","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5848d10c70df400872386a25a6a9acad1fbd155a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klbfry2t.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-1liDYtJtNCO3u4nlsPX6d7VD2HwFeUzTje+OwEXQZV1CozgB7SPrk6IyPBS5zdJaTilEHchRNkA9NlZohpcYMw==","shasum":"d5bea9c4780dfd714bedb5553d41a46af2931cc1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klbfry2t.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLuo/CRA9TVsSAnZWagAAr/8QAIMMVUY2kz1mATLUkgSR\nK1AYsgobmEHpEa0uPAWWIC1ZgsT4Knllq5E+O0l2KdVu0Bp0WEd6eLVBcJS8\nMdZ1Lpq6hftHNazW1e9YEdJKm/DJ+yCfGuv1pW0Tp526h+L+2B+cHO22oypn\nj5WD0OTiQVCo55FRLQWcOtCifi5TQKjHIP6eL/MCtEGyFW/BheZjALttMMgl\n8ksNXLBWiauHKryQ/oMJoby0oPAXtOH5F2pNM3UPws5l4F38Xj8NDgV6niMP\n9O95ksnjVd2PyN4PRC4/0E+NXptldYbJVCOBn029aK+4qFtxYVPr6acvQSpx\ncR+iE67siFg9etsTEJofst4d5kz85A7CN7xx/iQr/m4xG05l3rNr6okgYcz3\nhLsZaRfSSmj9K5GveUHo/sSfeF3tw1Bn0mGR2d0l5ZkIfo3GrlBnJ4Tqfqbt\nnBxzj0mEa1cfPFV6AoS66qKsymgM/zv5te3HfwhW0vySDA45jqywDZlgaNu0\nUvw3cZzsQSin9ojlWCwp7bJuIApLVqtxOWCRdcJx0gtWk/jYiWl+Nq1swE55\nkkMuo89CtrqJZdpAacVenUTVWdZCj1SETGoA5GcExgUW4itKJkPD74T9u7v9\nv6cXkBSnOtdZhxLSFuESTkmMbUqVQXCcVkwPZTVNWg8NYpZjjMhAztYgdflY\n7eu0\r\n=VVq7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMP7J0ufFlB1LcPsA1XcJwH+jyiwd7lpC7H+b4gRaxaAIhANO0afTJ0CWh6ArqF6JMjVuxHxQ+2S0TVQPcrF1QD4b7"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klbfry2t.0_1613687358806_0.4934167656715218"},"_hasShrinkwrap":false},"1.0.0-canary.klbk83lx.0":{"name":"plumier","version":"1.0.0-canary.klbk83lx.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klbk83lx.0+5b1f426","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5b1f42619230db9be4cc7dbb1b27a9ff5a7464bc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klbk83lx.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-ZvjWvDtWGFfQvU/tNtPvhNjpT+tAJPKIUbXhOuUvi30NadLQh8cOUBcfM7bRnvyWxhF/X9hS8L1QMFHruevUbw==","shasum":"10c05821515e0f498d847169d626b011cb3067e0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klbk83lx.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLwdyCRA9TVsSAnZWagAAHF8P/ibv1Xg4Jz2/2RDNQ5Bx\n55q7gCobp2BzNh2EYqnbkomkbNTg5hJxG8iyKnrmjcHauZ+v7ZAYru6ajAdd\nQXBcbtcuGPBaXyoFcBMMpQueFszgOmvvCJpPX+ePPMTEQst0jhqADWoBgjHa\nGClNojX0zcG2LtySzQGeF+AAi40m92ugKTE9GZYBKxneDUV0PLGKvWIVWxQL\ntGFZj1P05DYDT5R82nt2uIDAtlxH/JpqaaMc+XHym1c/W3wrbg3gtZP5Qsqk\ncGdg+rpwzH45e1woedh63mnrH3HOcXqmpFI0AFmB9cXdpwnjSeUBLGhJ9dSK\nui8kxRD7owTE3xNWFoyV0LFmIHM0Ii5ZyGxweDvWmG4zWjm0CNrY5dTY5052\nE0z2dwjxUz8c7COhpo/jrJv4emG4bxsLlWN4kiofkPOgjqMysipvy8hvwVQf\n66BgTpjnqbD3b3BEh/FnzLY6G7pxLDXzztPT6hma16Q4ooQO8O+h76yoMidm\nsAOc6P845inJRcjbhMPLMF+91537WD6n5YsZr8weFR8C3KyCE2M20D2pNV+z\nlp3kqu5jaW8OU+o2uHxQXniuaMOXwZwwKJxtxZ7aPaI7ip6/yv7CyaRIwang\n1yDr5K84PBRq10kXCna86XW9/iexUr6f5tvx/YR3/dweWdk6+YR+2zSp+nwL\nWj/r\r\n=kSVW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4WyzogRWOYZ+7UzmBoIpYKn7XCxH0+PnqDU3GcRBz5gIgYcYTN4+qnHT3USG/+e7KTsSEg2hskhWnCSLfH28FiQA="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klbk83lx.0_1613694834067_0.1910090091469001"},"_hasShrinkwrap":false},"1.0.0-canary.klfp4p97.0":{"name":"plumier","version":"1.0.0-canary.klfp4p97.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klfp4p97.0+e2fae1a","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e2fae1a3aec10aad5ba9a0d079ee28d714d07832","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klfp4p97.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-WMEVyEzVO44I4uAezdK8hNgZ1ET68vSnttLvIeb/cUb09ozQ30w3jgoWxS8ds49/htCPMVy6B88Y/PV2fy2Bkg==","shasum":"f9dfaff28626295ba411860ea07267c61d9f917e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klfp4p97.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgMthpCRA9TVsSAnZWagAA/kMP/RDW23oXSP6UW0QlCFgy\nvFI9e5kZxpFm0mAR1g1e6tqlhMaAifFlfmcG06xG1WXm068MFXb9Uicdf6r7\nHVoMwvmcU9Yi4wMq5FsEwwjblT/lWjqH5vGMlQ+0Yw+nH7puN8++zXDiz6BT\nvYF26BHV8lYTaCEwFNheNus9zceYEY1KhwHfJBrxZjMRiav6emS7tJCv0tJi\nNtI8yJF3V6LuT+rtBCaGgFa3XaEBGVTYBmLqF5u9ddmQeEwRFqrnhuauwZX+\nSnYCktJupk3kWlBrYnkAhuwAtPO/m3bTvlO1VtSAl5U8ERwxxTkZYP3gR5Lw\nhWj0mh5EuYWjAxcHk7BTmlJdZzjQI4qq9sY+xa2YjLZyHewNb6xC2dvkvOlH\nlkrh49eF5JJrSo/fRh0kgNDG1UrpneMKC0zzAG4R0ymRqu0E5LdMwsQBEZxJ\n+Y78byBgOaBFBG/DJwfw14brdeUfYoupT6eObfyJd3MhSx3/6Hn2zUUEWby6\nKWcitX4wrW07emeIAvPL5qBGqEM+tHuuJSh+OfoPHodi4LVAQ4dPu9FdHnO/\nEe1LtvBkvC6FbtbMvdaSg3TJnmYoV2UdS5BqBtRvvUIa8Xu50gSq2UJByjYe\n2XfBVAADBplEPqmpylJYPFf/IT5MlExg1kynjsV8V4KB2yy1SmdhS52U0nkG\nns1m\r\n=6nhX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmUIdmEzBLmh/KjBFFvi5Sd200vsH9AIuoFMhXPJziIAIhAOwP9u6jXOPgvraAFeRgINdwl7qxo0NyuVqbT2aYSr22"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klfp4p97.0_1613944936820_0.9350878246326553"},"_hasShrinkwrap":false},"1.0.0-canary.klfrodyc.0":{"name":"plumier","version":"1.0.0-canary.klfrodyc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klfrodyc.0+9db7678","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9db7678ea22367af626ef7dfa0dba35533464d52","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klfrodyc.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-IqGTo4v/6XbPl/9pYkEiALMibqwpPzgv68SDJkFrMfIoxVJ4XaT7Lgnq1mec7BkiOo9kIgGN8sJ8VXVor0QyIg==","shasum":"5d2bf97f2bfa37a2efbae3a5958243d04695f1cd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klfrodyc.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgMukcCRA9TVsSAnZWagAAvrEP/0yRYLxuEjvuWYnNElhk\nS9sRhcHHLzwX+e1G+mzHdN7ERrUAzznhtLLS6g02LwHYN9uMFDR17Fp4CPrX\nPBX2A7K1Rc/Q+cdpYzQl1fX7/5Xq0xUPWHw/8VESvKYG7gDTJuore5TpVjcE\nuuof6fymqeEmmC5uJNFdO2o28MSMLr4OyN908av+73kZ+3IukvtBfdWv/0hI\nSTLL1/lneT3K/9d/+YhhBSIHi8s+KrYWVAzItMF5eUz869RoaAiBHzy5sXod\n2XkP212yhgpAr9n+RmTb+jf907fyyvNYPfFBiOqJyu7+3MfVI8fGCiDKT5Yn\nEnmSvbVZ0f2EkAxACg2pWyI1YFzWuKrYD2fRaZr3CzqALNg6ZmpBbELpt9Fn\nNo2knYA9vClvaaZSqbIGRncouRz/lEdtT0WnBFQweX7eYF16V3rBJ50fxah5\nhIzCj3MkRYMzzsjyUIOKsK1nRR77q7HvRSObLoygIje87DV2x693Jdi7miXw\nThGqDwMCcK8VFOYxLSU8fMrcFNGnKDAPMErmwfmA2LL54fe9zKQqsU3lRCPQ\nX8dVYfUbM4LOGcGoGVM77sOOMnNvwIMw0u+IG4srFcYHmFuznNfhC+yTrDFv\ni+yJc4pEKH1Y8xUUphZiRmG+UOWupp45xcTMVe8KHzm3aWKXYxuxyac8Rau/\nWsHM\r\n=eOXi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC9M3QL3sOIqPQqdwsQ1L8oLTlNKRYXY22F6YNzTSwZuQIhANafbSpVt3obh3wl+Q/0XajLkKjHEKgGxeW7wiXXzfyf"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klfrodyc.0_1613949211614_0.8808834132725925"},"_hasShrinkwrap":false},"1.0.0-canary.klfunam6.0":{"name":"plumier","version":"1.0.0-canary.klfunam6.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klfunam6.0+c427c60","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c427c60f0affac2591835955fd35981a1170dad3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klfunam6.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-hTdGf/+S0+VU0/jrlr6HVRbol6gy491q4v2e7I8egZHZ+24x2H3JKj3NRtCANEpFLrwmDrPqgV/M6clHa+XOiw==","shasum":"ee09505e1efe50c5be45daf005895b0655c2e6f9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klfunam6.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgMvyZCRA9TVsSAnZWagAAwgIP/i5bTh5i/a/rvm4FTPuT\neUXFi8C/fQHoa50Tmai2pTxANeMB8v/5laYlzYkDcQG1vqQe3sEcU/qSG6lu\nGH/g5pf2lZezveDAerSlhrWJFAMkpRUgu2ir2JwFwHKs8vCOQibGfUo+NDfa\nLoFFE/AhjkYXdhI4gQHf3xVBcH1U9WEcTXSbul25/PdRDIOp8PTsfN+TLwln\nqLSfijDn/pKyDzs5oraRY9mrP8OhJMhHDEg51dUNUbETTzf2bNyGnVsHzEpb\nik6sbMP5bg0mm3fRv9sHSAY2v0LulntZhIBA+3+FBiYLK1YA2gK9ceu694PF\nGcnfhjc5gcXRC1yUUpOj5Jna2GEpoZ2H+3o6xJZWkOZj9MUwNQJK8uT5Dqyx\nR1X3SM1rKyAw1TJ5tkw/RHG0ZUvaKDORf9vGie2k3tbJCNJC072V8gDge+/3\nAbMg8+spQ+ZhQwqPlKgAzYKMiDhP/rlmw7WTSKf2o7vsblEkqWKKBuQC6yie\nZj1UpwFaFY8YtSG1rOY3WPsYZ0SpmvawnZZ7zLmbv8lf5UMRLMvVDfebv8SY\nvCA4AC8pM2ICj4aPyMCPbS1aKO8hEgPjuHeBgMRPZCf3GH4wwbrYbhp5pD9n\ngoKEVQaZ+S0LfCtJfQOzj6jMnwNqJ0Pg2lISIQVJ4RbHMhM7w1IyE1inhaAr\n6yfM\r\n=XSsC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1hGzNyXUjFcp2bIRpCZXI7LvNMry2Sx8R3rfgocQrgwIgOAT7wIy41KCMIP2DF0UXWS3gmF5oJiqmT5kTAdqFV94="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klfunam6.0_1613954200902_0.756784459133613"},"_hasShrinkwrap":false},"1.0.0-canary.klhat76f.0":{"name":"plumier","version":"1.0.0-canary.klhat76f.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klhat76f.0+9726854","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9726854548779c60fac1c35d7f85e09ac1bd030f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klhat76f.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-JfYBgiX/x5fM5p5ecQgTEnWtDlugTViUHqWjelKSBdQ14RAw1Ou9NcNzIlYrl0KxHJaCg4x7uHsEsQtagBi+Zg==","shasum":"7a5b4e9b6f033620ef03c8ed400a37b9388cc29f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klhat76f.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNFLWCRA9TVsSAnZWagAA4F0QAIHCH+EG6+VlWWptoKzQ\nO1WBNGjTnbYDuaDLvQCmFl5RMqi3utpLIazgWia7S+zCZS+WqmHGl2pFPsS9\nkCXtG0iM9J8J5aTWEURhQVOr7MsnlXK/3DR8yCJKqYdjEnHbHM6Eidn5YeLX\n4JgJMxqPP+u33BPRineqQZ6kReGvNb/zf0QCAL2chNLkNRuhdKerJe6Z8kPi\nmy4Dg083WgOPXs/OAX9BkApSXZt/vqJPF6arO7IOKV9+BNEIO2I9bhZuJ0wG\nQ8MNupzYB1M2BZjBUa/4PC1aA2wKFuBqVR3TIhakX8TRcH5ivSeq7ceWKxvu\njvtBQeI2eBlQ7z4KtBK1rV+fQCRLcKAXWt/iQAE9iujS5AVgqsQdyMxQ3zw+\noNdPAdSDxJAovLPf3rHm1ygEPmbRDIGGsofmJExfI41OHUt7Fis+AZjb3WKL\niB99bmwrNTYYCHj1YRcQwYZK0j6DL/9cwjwUtpRho1mQYWeNe3ETHG/4Vlpa\nmZCiEZE8oEZI44zeBAEk56O/KDNkoXhiprAvR6XcpPnWBhHef2oXlK880tmd\nQgzBGdZM3NvlrjTFQJWkLsSAWHby3b4i6dhP5dLm0jzJBoXi0k5rt7YmDni5\nILfQn2Qn0nNKVjWK5URtPvxgVIvi560240T6N6NHT5NOMa58dW1M+KCdPUI2\nduUH\r\n=NfZK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC1asu8hRW+rqij+ZQilYrq/d2o1b1juVW5WQqwxjtS2AIhANYXyqxjmp+wWnsuSbtFYjve5fJliAzJFVm031JbC/d7"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klhat76f.0_1614041814536_0.40502345513637694"},"_hasShrinkwrap":false},"1.0.0-canary.klhhyfma.0":{"name":"plumier","version":"1.0.0-canary.klhhyfma.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klhhyfma.0+97393e2","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"97393e205158117921328713b1fe03eec813f9a2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klhhyfma.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-m/hI/M13qjIVUiwx79+lQ32DrGX2ki8ptcIHSoo4D9nHNdJL8HY6ro4O+upOyvx8jIcjRYAY86cFOEasM559Uw==","shasum":"2b881e4ef46f35352e71ccd6675a7ba8693305e5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klhhyfma.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNIG4CRA9TVsSAnZWagAA9HoP/0iytAoXvIhc023sL/Qv\nfvz6G5/j9u6Oa06oi5uttiIPvM7p0i4ywicu/rQagtKDC+RFlBMtAz6Qo7m/\npQ9Foianr9kQAJ5uc8CWsUE+x6tlBeL0DoRToxbi34slPj/P6iIbKujci7DI\nYbfmRNiO6lX7s8cUg9wwO2VgQgKbo3QZ3RZOUNkCyAs9AKUCJC2KSwXBAOHL\n+hznZelU91TGC0UVUxkkU1k9bJIfl7aa0uzNhzCAj5euZoMMHtNtbckViZ/y\nVLJQZ/orGe7tapW3VcWBrJUwW2QR1sZqYVHBx/t2R4Ey4n/74i2IyyYQqbY8\nVuOCzvCYglPnLpXvqUlYM1DkjIBePwCZAZ3aJMSROU1LAKkdLeyy7AG5nE5D\nQrc+AAzCLsaH1JNQE3ZLJ26Qs8wIT/q4dFoA481ZZlwQ4aPNCsJtZXKaGmNX\nr4Xt7njcwZK1uUmOwekMXwDKorbYdBvtm2fKJS/EJgpGqXNnFNUF02ldg4m5\nqeCMZ9R2jQZpwfbmJYrqm2ExM7milZjtBGMS4W0imkq25KlPJcsL6I5gjYrI\n4AWsxt2FEsXkEEowXyJoaUWuIukYVBWn4AX9aZ93vVvcovU1xQy3ZtFmtHGR\nvx01xJicD4sOzPUjbD3tHkdeoTHkqLLgpTfRlzZyyUSPanOuQhTSFWFhtosS\nWFPK\r\n=tBfZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIATdI/7omz+8qkIq3QqdB48BDMdoRQhRaINGw/olxam6AiEA6hoMra9Y1SrYXAMk/jmVa8pNobYugVrWPDYOFhFT7yU="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klhhyfma.0_1614053816266_0.3062486013989971"},"_hasShrinkwrap":false},"1.0.0-canary.klht2f03.0":{"name":"plumier","version":"1.0.0-canary.klht2f03.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klht2f03.0+ddd87dd","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ddd87dd940071e66ba5bdbb91e7960551273e0f9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klht2f03.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-TEmKGbavXjYtbPi0/GezP5SsSWhiwg6l8phkCzQl7VIeSNdJivTRByPke+6leyX9psrAukIVSN4AVDUWOaiueA==","shasum":"10e91f0a1218751919b7608fe7e2c76adc5a52c9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klht2f03.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNMqhCRA9TVsSAnZWagAAdWQP/AprDf9mGyXP6aekCWqq\n4FphaGvO1YIWGW67+iJiCy5tPekGr+rtUNCoHUSFd9XqS5YPKd0ggHRQlcNB\n/JR9vrCZrGr+ZIqTPP3saxzx3B2UQkgImObTPycvNS/ij3zwEEIaYigk3yXV\nIIIpeOkUUcTdllI5sgAQbIrsmcbQpWoB/2aGvv3yZkHNHCoyNqAHuOielm5h\n/Sqdmi3ol0TrqYDDad2NZQz6XgX0xfX2nXYX0OZI5CByPHgWU136SnWnI+oy\npm5CE8nXKDQueRsFUtl6/5VUDlwYEcZBoVRlIqJG2B8UZJdXAwuLLX7IxdMT\nfI7nQJkXEK5ZUt/Ub3udmVVX5WtrBt2caSBLnjEXXnd0rnNH2gFYRJs+wSmg\n07IC3D4eDWNim4T2ePdtYhJjdaB0HBElxGVOnOOxVUum7nSnACwB8j4W36MP\nOqy2+7YF2W2FD8h0SRadi9OQK6Ih+2Y3IzWcYs1xsjnbsD4PrSYO+072tUZC\nChHpQ1y5IGxGr/jODSGhNVG91YFs3MMPfvm1yZ/0jQRWickWMogdgu0dmKxq\nVip0uB9Pduj6ar/FB5VQubXI2orLUv18rh5g2WMoBgFFif1/wTEr84mQIpBX\ni30y4XXghcATkyNtFMKE0xi2YPcS2fgPRtd39BVlMiFAaxvtp83hTBAcA24D\n4OAu\r\n=oaEw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCdS4OJp0QpHK6txGMVxa27PZsK1QItaoC7jyKUJqDc2wIgLRyKFYQPzS6IbW/K7qBKn5MQRHKL6RQmPcviwLLnWVk="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klht2f03.0_1614072481148_0.851195087888903"},"_hasShrinkwrap":false},"1.0.0-rc.8":{"name":"plumier","version":"1.0.0-rc.8","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.8","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bab5f06b42fcbfedc577f1fb3270064c633e6811","_id":"plumier@1.0.0-rc.8","_nodeVersion":"10.23.1","_npmVersion":"lerna/3.22.1/node@v10.23.1+x64 (darwin)","dist":{"integrity":"sha512-tDCE2GoydJjoOZOq0UPu9h6hal958w6rUmm8pLDuQKvOdQsG/T+dj/kocZuRcOmBgbWlZ+noGPkezIZeaW8M+A==","shasum":"ae64874df62071559c04d8836dc74197fffd07c7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.8.tgz","fileCount":13,"unpackedSize":25549,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNNG0CRA9TVsSAnZWagAAFG8P/1Sek0Y8eaHJdTnXxKoz\nvOAnGzIGs+iMp+nhGi3Kd7DjB6rjDORr4bcRF+aUkdrviRM2RJGd6naPqAqT\np+EC4sJqGJzfKkxiWmFsRuyaiL+JiqC3hdxxiAIE3QuWqdusEEKEiVb8OSkD\nsonhZqw736T8QnYeZtJ15apCstKAHtxW2i/xynb+TVmFo8NB6gnDSMm6Uvvd\nTd0DO1gcDmEFNJohP76IVW67Dq1I409r09L+1J/XMQ2dHd7UfieAJOGREQhE\n4gGuxMafeUymDHM1WBXPOZRSzVuWae+Zg3hunkMEdSEMkEI/3TN5hJlJ34MX\nGlDFmMip0fhdrq2BdXDCVyo67PX4DojgZmuYfrbiAp2EGSv367xZhOOVjR5j\nX7MI/KAhXeVFFJVBAMablJXrleuKExaJdEAVxviFZMikse6xtheBh6n6oWHN\nfPvFeqs3fPfTdSgiQYV5v3W8NF7OE/2FneZBPJYathW6j8xwytydUoIXS2By\nnn4cz9FZI8OL4w6M1Iz0AMPMbppJ4M58qKlu5wg8Hn3/99AVQxFjH5wMB66M\nfZqfi4xcRWZh3c4QjsaG6QSu0HFoYcOHEM/RkVxpVcX38A9XfvPP7whAN09u\nc08TxhKmar2raA+mr5U0KRlbKA65IXL55ifhor/v9hUt953TtOYOmpvgkCA0\nLWd3\r\n=7euV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIChwqo5w6/Kk7n4MWh7Gu5MUnREoiSbp+vc0Wj2uiNFdAiEA0RCOYzEvqmRHUgE5zxsD02/cY1DqomBTrbsgiNlh0So="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.8_1614074291746_0.6602703447805351"},"_hasShrinkwrap":false},"1.0.0-canary.klhu5pq0.0":{"name":"plumier","version":"1.0.0-canary.klhu5pq0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klhu5pq0.0+fa5b8be","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fa5b8be6fa1d463c956f03635dca7e4faf9cdebc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klhu5pq0.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-amZNOFJZog6fXe2l8+3/KuZerR2HGUukF00neRq2lUGbFwD3OPrWyf1PW4hpEj1Tkvq5d3ADvNJh+LlodAt2JQ==","shasum":"7d7fde94b17f6f0ee487b294cdc76f1704164732","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klhu5pq0.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNNHqCRA9TVsSAnZWagAA2J4P/jnTJr9DJLTH93uUFpAa\nsKY291TErTBMiyDqhmlhlBPq7C3r0DBeUlb+a9sDfoL04mjrJqqnS/NmRFg0\nIy7OT4o18TJ/5L52TZkbRiMYuG3TSXn6hdsr7Axu3+b/1qxi5mZ6BfimS/1b\nuQB+2hrlj/j2WLIN9sRQz3gj+OXyjPdpiJWeAaXjwsQYP3ZgYbQNcy7t8foi\n34HIw7vZjpmsRJAU9P6l5xRRZSVEXeLklRqjS33d1NqVjLogEqeGxpfUx2vg\nHOJMD1bNjtpHq33chndv/C4y+e1Inlg/p5qy3vcRu76FVOBYEmAZ5cHc+hLY\nsjaM6vFdrBIqqTD5V0FA30LhkM8CNgZ+kysYCc8naARMsVi9rohWZaFah6d+\nmfo2NOCQHFCYxxyPGmoaK7FK/RT/BZm50F0E+ewHUU45qSAisU3XGfxvaXNw\nfVzXKiogmw3r1745R1dfnoh0vkG4Hw8yOavFHb3XDV3asSYv0nmlw1As2tvS\n5IItmDO1YhowV0q+2HNeEnxuGZ4HNZaBEZ9JwM/zc0WmbBz2LV5t6Ffjdp8v\nru+xYDOrGKuwq/Fdd8mrhMsLxT8kNN/WShtCojfYGkzwf+KW8nURRcg6H4uC\n1Zr84ubnQf/s+hF2Z4iMzpEgz4c84H1hodpjbJw424sllDYGAruqZc0PpSZJ\njs7+\r\n=A7fZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDvcUq+hlx3y+64+MtB27Vb1JBzBWAoiIl09aK42k7Z5AiEAtGVzyfwPqjkZIN4oObI0PFLw82F6kdSNQgzwVnp6j30="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klhu5pq0.0_1614074345804_0.2873312021262022"},"_hasShrinkwrap":false},"1.0.0-canary.klhuaxso.0":{"name":"plumier","version":"1.0.0-canary.klhuaxso.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klhuaxso.0+bab5f06","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bab5f06b42fcbfedc577f1fb3270064c633e6811","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klhuaxso.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-LECWh8vK+sD8eWnVyeI/DKPwxrFNBfZyWRG9xvgr0IbimiMUaGCRPD2cS6xsTpryhwXvZMImtQcUNVBnPRXR8Q==","shasum":"a8172b795c72662a44c6739a3f37d59367406b1f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klhuaxso.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNNK8CRA9TVsSAnZWagAAB/cP/2fPa9W53YAk/Ivws5xw\noHOye9AbOCoihcFKMEdwYuNMNqTAse1brucZ1cFYNcmUm5+2EQoczMyFINvX\nm1mh+IgyEaf4Nctxd6IkGFC62ph+XQ+hXfQ20bjXcNAGvVoWT4gbFxnda4xg\n5YavrFsa48wTkmBuhKFfaw74jmnYAFvE+kgHcYoqK9oljRUowZFUAo1yM6L2\nKzbbsHpXZIgYA2KOn6RoY1uVAEAF9HTPNwt4a+vjB3RhGH6WyGqvEeIf74FR\np2ChMffj46PALwBVgaXiBkuoKayXo11+YVh3SfV67kGlJcYamUBh2/RX5PiA\n4C55MDjofZlyneNcnXEPp/4zeFnan+9a/uA7hGPqyxWNkqS3JOwv9/LQZNrB\nT6TuVFfOQcj0hymBWJJSWIi8t0VQECTzWJvsCrO4EpfRCWlCcTRcByIP+uZh\n0CcE8ez0fRUSiW0+SWeP8SFlbNSa1efE6n7gVpfBqGoEe+GNcdezsZSm0Mbb\nRry3gI+dCb8rH5yg9uXRt3djmD16gKcXEb7OWJ8KxhZRr+nhBvNTZGdgn5W1\nqZ+CPg7zifnkIrvpdQv/JStu3xh7woc9A47xeRvbKZLEzKHnThv5c6sD/wjl\nerkDc65BcHYZc+Thw8Rk82e71ydO/UlrexapUULox6TFc+XS2EvMko3VG9aV\nNioL\r\n=DFhl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCNfLncUeKWr3VZrsEgXw2TRWBf8AUh9hp4trEc+AE0rgIhAPmmDMpSr53UCOon8AnVB7q5mPkcKI3z6VuetcOOPXxH"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klhuaxso.0_1614074556034_0.24501724212071108"},"_hasShrinkwrap":false},"1.0.0-canary.klijm2be.0":{"name":"plumier","version":"1.0.0-canary.klijm2be.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klijm2be.0+0543038","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0543038547fc130d5d5655a359bbff013d6cd92a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klijm2be.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-L/M4Q6TmJBP9c3PrzTnek6T2Zj3RAaCH5Y0FFLIJpDNx/AmlbGj7AAcKFYKsOqant9/5rlz4Yp3bgwJnWfSIYQ==","shasum":"d2c28b27fd1ef431cedf03d4f643adb99a45ec1f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klijm2be.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNXjOCRA9TVsSAnZWagAAjtoP+gLlmlsGkY+gd0f2re2v\nAXwSi5jVdPywx064wF+70QVe0j01TzS0EvRqhBqJVMH55G5u8GmPHd00cZ2X\nDLuiw0hEuJnYqNL9gCey+Y+4//UgTF8Fy7okCj/HCBbwBiK748Q0G8ueqZfx\nUWY2XkwJiHR1+gVOSNUOSJUNal9d9kLmOkx8aIQHq/+TupQbtKPG/+IzUs0N\nJFaRGSXkqX4XatYHtFN6xkTk1ApJ5kEy0aPCUIiOO3sfkFZTClkKKW36mUn3\n6++c9BbnTrx22D0f3ObtKVRs/9cQu1VODmLLoz53UJ8DV1DEWuJdhkf4YToO\nn3CdU8JYuEM4gr11kwj92id2DsgBKfj1x/qO/ngmr+8wvlyUnC5OhsqS4CHA\nZeUrpPoHTv0nq8J1zx5NQzR/qhEVrg4ZZ14dm0wmeKj/unCkFDM+XLCLID8v\nFDZ73wpvQEoqA9LlpqNAr4kdfOldRvn0H8a2fTR2g96JmLJClEyafgTmTnhb\n7pQwKjg5EyvhbTn6LJfZafaIprGBwSgVTmtvMlevb1AH6oLtOzENrD+X2UAy\nCJu02VsPCrpAwrMOZK2lSfkD/dHhTDj+X8N1cQcbuDIa5sUU0E+uQftYwdnV\nM6Z9KO9bWOa0en5CpSWj9dZ0wt2pHD8dEgZGXUDSfn2LJROE8baGokqJKF7x\nM6I3\r\n=9jXW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8Rp/1rp2JFb1j1nIDSVUNIKKL5FJ0vfODRgUF4xPC9AIhAIrppFxSx/OVnMsuhVmA4IUCKK/W0aj3ODE5PzxRhA4z"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klijm2be.0_1614117069421_0.8745077344026979"},"_hasShrinkwrap":false},"1.0.0-canary.klkqupsw.0":{"name":"plumier","version":"1.0.0-canary.klkqupsw.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klkqupsw.0+2f70613","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2f70613229604ee514661c31880430d73588aac5","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klkqupsw.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-lgDaY7FLjVkevoAv/+vVWn8/07/+2QaMpneHpfpj2BeM+8nI2JroSeI0tvA+1VsKUHk+8Weksn0UuSEVgdKHCQ==","shasum":"9ac06c8c111ce7e0d992e77ba12446e890fc336b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klkqupsw.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgN4C1CRA9TVsSAnZWagAA2m8QAJLbRFGPEv9pgajXuUDY\nzytBj9SoXS2TdAWaAmXa5glR0pQz3kUKN7GbjKSBaBTR0I7vUlSrHb7aGFwr\n6/djFEnovhK+cXMKSA21dMcTcohlLqrjVRoNMDnmpyfmj/PlLIAx6fSVuMp4\n9kSTdK7QGViirm6zEEG401kKeIiwo24szIZLAhS9PqyBn2u3yE5M3I9sv7XH\n6N417dWxs9K0fJU3k+nCz28Lv+Jkagp8kZgOQkfs5Chx4I/BAdCUl18t02NX\n8ga1Lc+8cJMig3OvUs+HOOt67NPZqoL+L2uoLznO6PkAw+W5jKZe5jsDaxMe\nl+T74s2FbdX1/9zFBi09y9a6dHufzBmvy2x9OGPtDAr+g+XI3h9VAaYtVfHu\nUMj9Jj4MWaC1zcxJJ3Fv6cUxetNYQYbDamTIGQFS6StjEP6I6UhforcEwXcv\nRiU01F7MexiHbiYI4DxHjZfRbtuwWwe+0Ly/dIljwxmqwgO7oMVJ5YpvxSRt\noEp5fMwfBojIyIjHDb96i2DEAxrumPmkVp88KVz/pcIGJ2e/8+wUcXs14tzf\nn0S1KdUKVCNFHqbrxCKlFgUUaPDKE3IMfoZM+cQpcWCHxf5mX/T71Frjbdjd\n00zo+EpEPIAQdIHvldN4H8k3AvASKQ+st9tKweMPk8u9YESLPSUQguzLTQYh\nDroA\r\n=QyRk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCdNoUOPOcbK1T1DtaWZC20n13ukpjkjtmpvNaThvraXQIhANFaQlXX997NHSdx85fmSulWcRcRvP95cSZTsVrNVuQ+"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klkqupsw.0_1614250164769_0.35744404658749596"},"_hasShrinkwrap":false},"1.0.0-canary.klkrhc86.0":{"name":"plumier","version":"1.0.0-canary.klkrhc86.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klkrhc86.0+dad86b4","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"dad86b4f58c13edddbfdaa9507586826339d0720","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klkrhc86.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-5nCdwWJzyB2TeezcM90BGXSOIYw4oiZMSdkk10kQoFVwBaPIQAW9LQWqR9tUwE3D7wGsHALGzsOHB3VUvS2bBg==","shasum":"9b2ba0dee05c7133cd60bc5c0424fcdcac84d9f0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klkrhc86.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgN4TZCRA9TVsSAnZWagAA0qcP/3WYLC3KuVo3Qk2v+Nc/\ntSGwBPiKaSQrEk8VSLucrFTDH8vHUbrEOeamK3decPiNEAspj1C5hWiPUbrU\ntdP968SFaHgyo9cGEbCdSLV2GHSXgl1daRcKAlRG8bjX2n8YmL1Q790vMnlD\npRTr8GyGGzRNdqquD8nQW57xrO6vF8iqe5bfDqaijirjrMz0KdSg0k5k/7RD\nHaD3jmaxGGodPj242uRnfVdhSSLtD4Pi4C4yY4Ie8rd5KFDkQs2NNPUjpToh\nUl/jMS52kO53xEWO6nK9DpECdhYHmOq+hRGr73YffvQojniWfkpTYeUfVOI2\nkhPSrhvHA7fvRnnHt+OymXHXRZwS44nZAfv2FytwwFt3ws8xBgqvLlayqRGR\no6OAYiXy1QnWyYCotkg0pFHKEK1x2bS7ohujP/Nhm4+o2kCHDN9V5ou19XID\nF4ZUueff1Z5sKFgo+d7XA3yVTXefbkmIHXvcqWRt5pj9bGrVm02Oo0NeADYC\nxGZAoE6yiLI9X4elJZYZfe5BE0pnrPcitViM85BQCYqKz13JIs95vXWct6VE\nWdCwTs73dTkugKtcNUiLH//Sc2d8TGcNJcUg84TxO3HQyW7xsttyjXl9oVnG\nHLBGwPlBKRoE3TiEea6Z6ZcSw40TOZsW6eTUztfSiUPElt5cK8t+xQKtgTq2\nJ3Ix\r\n=Ip4y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLo01RwCWGtGqMCWmzX5JmEaVIkHDweLFQ2h8P5idHLwIhANKFvwFveKISGQkHvXCJfEITR+7UmM+qYccEhH/VvEI5"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klkrhc86.0_1614251224798_0.03684473304766289"},"_hasShrinkwrap":false},"1.0.0-canary.kllawr22.0":{"name":"plumier","version":"1.0.0-canary.kllawr22.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kllawr22.0+75134f9","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"75134f9fecfaf6b93b79f683f14bba3a78990bb0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kllawr22.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-yLe3zcSXeQTwQ9ESpGi65TcG3QIJhv8CZjg0KgvykozZMRGHCanGe2HkbsYiIK9zH2BtD+PzSsQmWdOO51Zcdw==","shasum":"1d51718bfbd411329013c1fa6440850aa816bc35","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kllawr22.0.tgz","fileCount":13,"unpackedSize":25591,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgOARJCRA9TVsSAnZWagAAALIQAKOzmIJd5QZdf0XkvIio\nMDrS5gTNk5xW4s2JOgShWLTxz3QRAg/xK7WuFGkvilZvxnqhWnqUpJJRnXNz\nQV4NRYoiZQ/ulUFMrPpT+RBGZxcH+5DZfJZO2sNPP7EXYgy8HTCsn8pQ+BHB\nQjZsktKFfXvE5JHa+yogy7QqWoEdBcP1CFZYl0Y5MipTEZb900Njg+90V5iR\nuhMdbO3ebYLxHVRf1TViROHa0YYc14U1Wks1FxL3Pm16vZtwTCJW3wmNGKtT\nLD7T3GrtGbFURNkiZ56qwtzptbbFWY3zom45pKccBb9T+fZ7D7bkOPk4EfuD\nv1+sZr05Az4l49sAL6H1iLT68MRZjIWO35XKG9A9KEygTnwmK3b9dxOtKpLs\ngxy9DDI/I9N2jFRWdQjVgecQb+6OuOJUjEJ1aHreCa8RfmcLMbRm3+Z38cW5\nbSUTGKtx30vgiQ7ri8rzoCJMTP7MfuXNWLSmQENz99uveaOzjV2u//gacdP1\n5XC0AfoQkyqpApHX8mOXupQioloQ3frMAwcmK3e3C5pEDLUAd7EMOxDMcBQA\nMr1itKQnDvJMf+UwtZacsgQxA3XlvEj7i18Fjoly9hefw5RlYB/VGDuECyTj\nFv7Shr9MhvX4840JRm6K0ebJ/lvQgvz0xksOYosYfBRqO0Bo/mSEzrROnyax\nbC6Z\r\n=h+EX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHXpjS2PoJpdWsonHNCrny+PLv2aPCOs1Q8OyO7lNce0AiEA3RExKzUqarD3qlP6y7uRB6KMNYvd4P0uYfXnbRWIulc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kllawr22.0_1614283849007_0.39745436521362176"},"_hasShrinkwrap":false},"1.0.0-canary.klljk0nt.0":{"name":"plumier","version":"1.0.0-canary.klljk0nt.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klljk0nt.0+b7f328e","@plumier/generic-controller":"1.0.0-canary.klljk0nt.0+b7f328e","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b7f328e8ed8650863b373f8aca21ffbf8b9d3baa","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klljk0nt.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-UgUKHWxl+4/j1aVRLP4jnzmUkkDIjEKe84olMiitzU9tGcKxhAILZO1avBRil8q4KqXLZOCNuZC0r7HV+54DBg==","shasum":"f03feb68380ea72ed42c8441a51d70936a0d9b7e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klljk0nt.0.tgz","fileCount":13,"unpackedSize":26371,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgOD0GCRA9TVsSAnZWagAAkUMQAJihdO86apTnUYOc7hUN\n8mln5GufOlkHmpg96sToqUiiVBEGIRDur/ev1/9q18mTmpUY+D3M2FjMESdT\nAyUmRmOqxYm9fxKcGX8pQYN8mxrImeBdV0xIlyPTjHM8F+KaQJHGF35C48iM\nvUPVPJNZgDkzPvD51f7I+pITRnpVjF71S4cxIaetZXq+zUCmUPQs/2j8+Y99\nQIPbyoWez5NdjZ5UYtHoRYeyylBqa8cW9u0bhnsVolIwtJmo2bNT0nXHKh1v\nEfDpC0sspyKWN8YJGOHes1ouHZLJrvG+0SKKNlk+nHeUysDKCDCzzEI4OasL\n2eiwBGHyHjZ7vPQN/vJ+wwgoCE93Yr5jlNWJ/lZPBffKP2qkpbNwAFu3h7VF\nOfwDKRvrbmWbHBDRtom4oyd/OxRVycqqWjIaakIeNshDriN1jHyGkNENHh8L\nESM5nItmOboEPhm9hRkb55011o/2pP6zZwEhV3W27Y7CD1SQF+YByuWjgZKK\n1J/rqt3UWpddKtl7n0eLgMicMI5ZjW6/vJbhPHpypVpUGQFWPoXMrdh3aBmF\ny8s4UzBELktXsmrkwp/4cpOmGbd+JUtiadB4ezSZuSrCgo9EZlMGhE39C+Aq\nUWps148TLT0VKvaMHpbnJ5LDVyqLLUGJLuk9yaNuJp4M6HgkFE6k2vu0mRwL\nODGP\r\n=LCm1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDuSqzBgPENL5OQc/zcwVAT36T7Pbqy2GUspVDCPNj61AiARjJHpMEbsXOhmCiCwh6SbZwmqlTK7OiV8OU/CuR8tog=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klljk0nt.0_1614298374270_0.3178862413433894"},"_hasShrinkwrap":false},"1.0.0-canary.klmi2vb6.0":{"name":"plumier","version":"1.0.0-canary.klmi2vb6.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.klmi2vb6.0+d7a6872","@plumier/generic-controller":"1.0.0-canary.klmi2vb6.0+d7a6872","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d7a6872abe5def4c7156811b7b4d4d1677e15abd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.klmi2vb6.0","_nodeVersion":"12.20.2","_npmVersion":"lerna/3.22.1/node@v12.20.2+x64 (linux)","dist":{"integrity":"sha512-oJ/U4g+gO6gaD6sj3hv+CQmKE/xk6g/+a52omX7B5STBaTQAcGXQfcsGdvW5AdgguLhnJtEDGjwA9ydFuBTEKA==","shasum":"0dc862febeb540df65d7531d8efac627a5cc0187","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.klmi2vb6.0.tgz","fileCount":13,"unpackedSize":26371,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgOR+NCRA9TVsSAnZWagAAmVgP/1xmUfXLkEDCAgPjHPcj\nzonHhOfBqlLQ+iefZHUq+MSuq8v2iRwR5HrZAmShe2RMKXsOpCgTqvn3g6Ii\nuomhNm2Fcibw8+rHbv0csNWwIjcMMdnM1/eBQOTr0RFcPWt9L2q0+EMTBpEx\nyMfvXMc7cPmD6n1SyPfjwbhZT5t8teygVOj4UGDi6yHhZW1w5k2ZS0j4jSCJ\n6yULKgW8KuatXglzf2nvT0IaYQqymR7cGwylEy5TOeBv8UJcGRbSpDsiGgOH\n9CYbpehWEoEOrDyda1QaQ+hnaMGQPbDVTSUNQwMijsgRxwazGafEBTXu9+Ow\na1ufQd+KUTOX4VSaPqb+RczSP5Sw8bQu3y14JBim7sJo/Ra+VAj5QZ8eDcYb\nxmdAMH8Wt3vtUDk+yXFI+iSfmyp4kt5tMlaivOlufHvygHbi9FQJqp6ibbqj\nU6qq1pd+PLldO14BBgsBdDsctBOeZ+GxdNhrEb0mm5AnFNiQMzZYLlKf1LZY\n2zjkFMA/f9HBjCL0TyScy3DrSgFSPh1kB+lpqmmrcWmDItw4pRU8RbmR2Tpx\nZQI8F83UqE+VicVwQdkqNXju3Bt2U87ne0j4A2fbdVnlwWL08EtnPox9h2N/\nlHO77YfZN3YfmQ5Wr13SKt1NYbeeLlXSsIt2yeCaIM86H/+6SW6IworOEPuU\nnM8s\r\n=/3bp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGVtdqEHPaC5IfddypL0OTQHqZVv8CB1yEQdh5WIdfS4AiEAzL6LbbWQCAAAI2QPBnRYHmyBhee5OlOE5cslbai9yQA="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.klmi2vb6.0_1614356364953_0.5026965972220427"},"_hasShrinkwrap":false},"1.0.0-canary.km12ir1x.0":{"name":"plumier","version":"1.0.0-canary.km12ir1x.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km12ir1x.0+80bc699","@plumier/generic-controller":"1.0.0-canary.km12ir1x.0+80bc699","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"80bc699ba9743c10db02a287b860fed8888fae82","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km12ir1x.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/3.22.1/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-RV9o3SCQ78jjv5lnDHw2iYTQRtrmFVHRoOJWcFDxBofJg/xlGXoeXcvSnouPK3PK8UhNBAPv8GfE2qWUxxSBwQ==","shasum":"49f54a570173d52603bbe16156a1c0302448a48b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km12ir1x.0.tgz","fileCount":13,"unpackedSize":26297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgRpBuCRA9TVsSAnZWagAAHAsP/i1W6LwIfxy1grZx5iZx\nuN39yDzu/u97EsLHPzwkj/Jcv37TdD8WUxcTsxLD02Gzrim9xCupjbO2c1Mf\nGkPrbV8OoZt3L6Es3dFKJBhc/Mn+OMZhc6BaKk4VqACMGZIct5hUkUbdwc6J\ntCKqCSir4Qqqw4vCUeZF1cABU1ODn3+uXvUkZuS1Ccf3HafAcJarja9vJpff\nG1lZaEH7u2Bgzgal4TL9IunxAA0L37QqigxPhViemZhYQDJpXJpJqqy/OjtP\nb8peR0VPJYR+kjtnbi1ZFXDlNuBPsGVNTjx38+wBeTGN5TS5ZluY/9hHqODn\nqdeVYhL50cdl0mG52eQf0oIOask+Iaqyy7zrTFPhCP+adqye2zK9VCFqGnHx\n2t8BNLDYxdWRLHpypeADkhd25DstGafcgppTuIzBHiVckXteO6mJ2Ie6ylFF\nz4cHNlIcvCQplu1OFCSxMBWQrvn6bxtnfKdyKxxIUllxYhQAMdocKmxGH9O5\nN4e/j44ZqKpz3HyfVNIP2BqJcqRXQsCoAI+hQuK8ei1AJkpxGVxVD29lSHEL\nh6jBSt+K26XZu3Oskt9xUTPe8jdmqhOXSaR4r69jKObnzVdzTIODBcBl269e\n9tsMicfTN4EzgejExolS4wZWBSnDL7sdQ5gthVlM8D8wqwGDmvP0ivsOff/p\nXLGi\r\n=aqea\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5a8rVRtC20jfrFwkljuJQfEcCvVGFqnFj4L4j4C0jaQIhAN0pz0i1s5pbNfzyf/tOXztOPkCZadZm1FdcAemvmOf2"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km12ir1x.0_1615237229643_0.9116475629319798"},"_hasShrinkwrap":false},"1.0.0-canary.km2nk4a0.0":{"name":"plumier","version":"1.0.0-canary.km2nk4a0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km2nk4a0.0+ed0966e","@plumier/generic-controller":"1.0.0-canary.km2nk4a0.0+ed0966e","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ed0966efb47ae8ed40863a5d426a33c4cce68aab","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km2nk4a0.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/3.22.1/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-EUuaXWOCmTG+J1R19VCJ6sHOrpu0v8uHa9ggmcqeGIiJQZC5qzJUOOzMDiIvhTGB87973IcrCOOBxpuSzEnQtA==","shasum":"aedcbc6e2d2716a46ee0d64b33c7bde873db3a0b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km2nk4a0.0.tgz","fileCount":13,"unpackedSize":26307,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSAauCRA9TVsSAnZWagAAVssP/1N+lusj/xHpvdJiwQVj\nJLUgXLTi78pyB7bQZrMMlp5wjVhWJlMov1/NxYZYKtukqpLX0AkcZbgCobG0\nSnHs1FuHuLzs5nyGFcZWrohkY9/DIE0Wdw3+CAbWO/xbHlRYqrpDTg4lenx4\nB0XPO0JOrHK1PwTHxWVeUZWlQT6m8FDvg/z5me+jXrNUtGS8RfCcq/Ln5DYm\nJg4qTWU1+iW3GUJBu1cgR5w5mwDx7EAciYW4UFnstiNwpee2F7AylF3c7iSm\nHNb5cc3szpo1+YRnNeogACl6bMMFRnypMaYDBEBP2ERdcejmJZljOxe0VNxk\ng+5scTbKON2NA2KSVGXDJjDyUpUbvw9fUwQ0tNQU76tUlPtnPqYdBZkxJjWq\nF1DB0UMI53tZUNdmC76IJRpN39IufpJ1eDTfsihx1pke4b6FpmbJf4stE6qt\nfj8ZzO1mpONMDpi9NtEnfDG1iOYG626Ihs0EIdvoOxDwgQT8SuaNsyffIA6H\nL4Rhs+frkQ/Te5jRxS4QR6x3/9ScSZe6y+WZ69d8jbu/4CK6ojCVrKf4OQQz\nnML/DJ+ha4ZxT24BygHP70iUrI0yYZS+rJNd8W2b1TWJG+8GDiyMzPewDFiK\nRfM0kYVr9weNljIlMHZ+Q8Jb6LmTRAl0oSmmTltMUms6J6yLNGrhIdzo5JC1\n7V7g\r\n=Ep4W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFHJ8ilvpV/qQ9c9ft4oM4Bm5Tk92TFTY936Jv5X/hFgAiBB6fvSZTJM2s/GjUZqO0+AU7HCZ+ugoWbIcPC6BVm7aw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km2nk4a0.0_1615333037903_0.9628991520179606"},"_hasShrinkwrap":false},"1.0.0-canary.km2rs2yr.0":{"name":"plumier","version":"1.0.0-canary.km2rs2yr.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km2rs2yr.0+38de8a5","@plumier/generic-controller":"1.0.0-canary.km2rs2yr.0+38de8a5","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"38de8a5fb7b3137d9cbc1be38fa570c2e51768d8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km2rs2yr.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/3.22.1/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-/mPjkMF3B1qx79Vyl3FcYy0u3LiPxASionnLykktMorsyAKcme0pA56Q8O+qO1X29d2hyHQZ0ntytTDfXVQS4Q==","shasum":"0530ec47829255ff4c5fd8a8e6e4aa6a387b42b5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km2rs2yr.0.tgz","fileCount":13,"unpackedSize":26307,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSCJWCRA9TVsSAnZWagAALqQP/3DSRVPz5ofgkO4pxE+1\nERuwQVucSTf5+iGj6q3Tjw26Wvucg8S0DU8g0pEIz1U+AdnODiiplw/GPtEi\n1tAd7TpROzn9sNuuYDFenHY3fn658XqZC/KqZHwynabf8bG5J69YwtEGSXsT\n/xYWzA/mUyijn+arptEyYY8VO4RbPeEaHdzHyptutF09Sp4yLZ10Cka9X+5r\nXn4n4Pqx47n0gAvj/62YgNcbKQRnB4Qqihym9jmmXku9QCXK4y30ln9ys9Wq\nk75kDXsI7CcXJp53QIR98QJsEnJy6XgDg9MI48z30M3kYT2xFm1nx/Xz/0qP\ndQ1ygEgtCPzEOrzUt2FjaBtzlttmCtKIoPxvr3T0Mu+/kdeJG3leGGdpZxnz\npd56fA+NL/j1Oz4PQvuZcBNTACsdtz13eSo+ul+esUDZEeP+fiFmYTrrtPn8\np5dxkrXtAe0GRWwDHHlZu8BMbWbOWK4sqNn/jiChoo4HPLnOnu4ZXXRIVJXY\nEt9vKaZlCItZddBmwEBMefYxzkkDIrLCTVT9fgxf9wAtVGtzgFIxArfrcJmx\naUfuQhNzNkX/Jv4W5dYENC0JmGAQtSbE5lT4ZV8whF6u+xqOvymcWBvLsGFr\nZCKjUbslx1Ax/aXs+kench9YW3uEJPK7CIYpVRXpED9NSU97px4FeuZm/e9H\nYtSW\r\n=IOpH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCyNht/iTt1k7Y5t5SRrTv+8qLKipmfdbKS2xe7FSU8zAIhAIo2dg15jwtdkpP8spTvsiL0b5atPTw57m0TjIoG/BgV"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km2rs2yr.0_1615340118339_0.10329714457749128"},"_hasShrinkwrap":false},"1.0.0-canary.km2s8d9l.0":{"name":"plumier","version":"1.0.0-canary.km2s8d9l.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km2s8d9l.0+71eaf97","@plumier/generic-controller":"1.0.0-canary.km2s8d9l.0+71eaf97","@types/bytes":"^3.1.0","@types/koa":"^2.11.7","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.5","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"71eaf972d7d458f3cec0eafab10bdf7256f6e93d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km2s8d9l.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/3.22.1/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-D/K/AUoax2CU+V+5w2W1e+t47eZWkH3wi8hn2rFt0OgNTJDQMWFYqUFQjd9iHjoQS3Xd4H41IXXkh0sgMCjOOA==","shasum":"204601ade4bfc0544356a4a0567a17ea6783982a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km2s8d9l.0.tgz","fileCount":13,"unpackedSize":26307,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSCVICRA9TVsSAnZWagAAKbIP/A4jHwre3otAdchRW7m0\nsyfSMgaga4r6yTfIxeBpeaJIAALeSKwhUQrN4at6pUsiF8jS+PJ55SsRd9oV\nVW6AZM1K4wN75v/nY4eB0Fw0GwjlTsoVasYLX1/guWlBxsd6cHbapnXhV4g4\n7w4r7SbAgtepuDzWmkLL5/mYT2u+rAomzGZ47MBitP1R+l8U9UlSEj56n/6t\nzrMvUa0c4i2CR+3py7I71qIlKC7oRVUlceJNdc5k1456MMYcHhj0eAoxtIv7\nXucWREjbuh9Xxv9LFI4lSw8vsAB1MlKkSZdYgIEctu9V/mYQeIsNU+sujt1K\njvnLO+1j8uFWtYq8uXfGb1ts8xtGY1FQbQRRPXyJiiWE6LHszTswzopR19t5\nN+A4NiuWY4jhhl4BAl+yHApl/WnL5fsgFuu7cGDyoiQzUpXk6WXFmbs4vxiG\nmW22UChN7MxnCb86O7vF5cvYgYajH54cO3xfJ6u2N86pEqfhM4+90uAcjMS4\nkNV83raGuNx3YQtBGuwgz5lt4dRGTQ2oX9uULtnj7H64uZWWmHxQEG0oesu5\naA2P3CMKUymmx2zZDMJ5hNRCC73b10TX8orPfmBnxy0NVWlRJaP4bAQqEUeb\nvquBHVsrPezOyoySbfkc4EUNL7K6AHxJJXMUDBd7+ukr8U4hdfPqoavJ/qLx\nLano\r\n=Xwyv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGstrLjDJnvX8muG1Oft2VY+K3HuzqebR7tRs4PRVDCAAiAoUz65/QIJjxM9YCOocm3TYoFkZo40SIk6FiWYAWvx2w=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km2s8d9l.0_1615340872361_0.36773992836803204"},"_hasShrinkwrap":false},"1.0.0-canary.km2vnjhs.0":{"name":"plumier","version":"1.0.0-canary.km2vnjhs.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km2vnjhs.0+e2a842e","@plumier/generic-controller":"1.0.0-canary.km2vnjhs.0+e2a842e","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e2a842ebd733b3b6dcacb8a1522f164ec2aaf3f7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km2vnjhs.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-9qi6hPrCgKhJwuw30NnxYQT02k1BKTqLX6TZ2jTE5VMNcFdsM+PO9FsFaar4jGnzYzHbKv3Y+5LmxFLGOslHyQ==","shasum":"a74a1224c3fa4366859423f5d8e8661c50ee7ea5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km2vnjhs.0.tgz","fileCount":13,"unpackedSize":26307,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSDvACRA9TVsSAnZWagAA3PkP/AjtOJSFOEtf7kGy2MP/\nbM35NqjHZTC0Jc0CE+EZozjsedXLMWF0okRyxe9kSQbveySl7xnZRMKaXOJQ\nEDgeiSKWMfSNLaiqsSS6RmjtD+R3uDc+l4b9TwliD3FLYct/y3F0+8ypTK3j\nAr1oL0EKIxsA4T5ANg+VTdRzMp31vFUFLdi12dHi7yEUua16pb+FVJJyGgkR\n2EZQFH1t0y2Iixaj+oapw6mfuYtXFB+zQ8PHImiZZtKyi0apIq+Rh/K8b3DB\nh/uF8SQlo2Vq6+O4HD5alaGfpPoAxh3zrNdZ/j+PUvUGOcTfZpVuHZDlHwyS\n9OOVoVZC7W1OVn2V42ASdUqD9lEa1sVmpLAsDQClur17f3K9+yKSmGUX17FF\norHZAl8Wtq12qd+gLMOqyh8YWKkzFYXlxbSF47iCkVmHPAkH8bw9Xu/cdsv2\n95cyb9cW7i2PAlNOe28i/YDYR468haxyprGAlz0yFzUDNekar6vQu4l9wPKV\nbLNABUi3ijgL5EyPB6huk773sWNu15EXytrnEtL/96dURpxW6JmM+Bmj3tCj\nq6g6534HLAl8YBrH7ybbW7ct8rWrOkaFEC69I+DV4L+hk6hevfjfnqTjejUx\nSdJtRg0zqHAkYu7aIIGE0M/WkvPKWTie6NwFUbNFHd+F73EYp49+fulW8vnI\nipv7\r\n=COw8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC7hOZPjClvapKqIeizhw+uBQ+6O8YtwbkJqwKN/Yn0CwIhAODick+egcKf6uMs7YwOwDqMmOnWLD5i/aQ95dI1GfwR"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km2vnjhs.0_1615346624511_0.34392561588471704"},"_hasShrinkwrap":false},"1.0.0-canary.km367x9q.0":{"name":"plumier","version":"1.0.0-canary.km367x9q.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km367x9q.0+e54acf1","@plumier/generic-controller":"1.0.0-canary.km367x9q.0+e54acf1","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e54acf1145a911dfbd1a52a227aa185308bdba26","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km367x9q.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-SA2vpKPkypUmnWVxZ1EKqeeF8ANQ3GQn6omeASFVzcGvA4XScN384DL1+9KoUgLZF+i8EgqTM5DTLP4jD/ws9g==","shasum":"d9e5bc7075c0266c593c250b7423b5520edd7e94","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km367x9q.0.tgz","fileCount":13,"unpackedSize":26307,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSIERCRA9TVsSAnZWagAAhwUQAIal9ipVnZpwyMIqp5ea\n5LuHeCQsacuYWTYaZqHPKRdqzMYuOZDInQG1/+bFFD7Z05NeVCWo7GkR47l1\n0tFnG8eKbjTzOo9tHpyuClygG7M8A2IMhuAQ3l5o5W8vqIqFe/QUY2WVOOCM\nqzpg+qdjC1KZSUys4MI+ZKDMpegbrXicA4d4B+F0rHABbnxJ6s6TmMX024Jj\n+TXFC+Rut3BBJgSeqWshNl+uyypyNfhuB+s2GcRoMULY9njkLt3A5kjuPFVv\nfEWQsvWyOlCsdFIkBR68rMWC6oDwfG5ySHxL46AFPxwp4xr91LOqPEsf6Bv8\ni9xUEWgkF6m8hUDN6udi+0P+IJ915602Uv5hRWPOeA6wU1yK4Ju45sVgy2fC\nRTM2kH9EhLvUxcrbZyZZQQmNb14gGDdWqSvNW5b568Qbz1Py5yZzF3sUoZm+\ne23QMkl92vsXv+DbXdGOE+sW+gg5S8EXA7OjL/9PUzRBO9/9b7MJGt1eNOkB\nSy+7XKbnHB3o4r6Ff8DH34smL8gC6Fmhw1PtELtxSwj1nbzAdHsfsi54M2uq\nV/By6fHZY9ee8M6tEgF3d3RdwgH6TYP9W29lp84m0TJRB2hMSWFn+CMTGSux\nPbPCcWQ8qN4Sx/fJmH+UjlBkUgoEU8l+VgQTPVv2fPeVeC57dSbNkP9p9+ET\nZKfn\r\n=z+WP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA8Qx5kjA/nVruX4M7u0VCUV9JfClSETNM1tfB8T5RlsAiBO66dwUiIXeeS6i+3xQjka6A1EN6viil0Ab7GnF+SmTw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km367x9q.0_1615364368881_0.7793512534934015"},"_hasShrinkwrap":false},"1.0.0-canary.km6t3qv9.0":{"name":"plumier","version":"1.0.0-canary.km6t3qv9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km6t3qv9.0+ba8ff2d","@plumier/generic-controller":"1.0.0-canary.km6t3qv9.0+ba8ff2d","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ba8ff2d710aee31e6ec9d77c3ccbe1a77f67fdb1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km6t3qv9.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-oTHTej6yzoeV4obo54TVwQnzXXnTXI6+J0xfZktQNnJ+4SqK2nA21IhKQuP+GKKT7dNwd5kiWK9frnoxqTwt/Q==","shasum":"bd3e1b489fe2117bae18a0cf8545a473805cfca8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km6t3qv9.0.tgz","fileCount":13,"unpackedSize":27190,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgS9vPCRA9TVsSAnZWagAA4PUP/iJ5Hodsuvzb/9CPV2BB\njbBxhmJgAKPmG3Vcs8IbvmvcNTYFoSi7A9LZ6VxIaTduH1pgeYkHQgaBt4q9\n2LI2+/3sJx/vwq/7ryOitkZjlHGaJXvBIibldsAQjwf7zuZKR4/PYzEcoB7p\nKaVO+K+PNeHIpT0Es71etpSRjsBTM/wZebcOIzdogaAvLZRHtOIuokiCuvlw\niUhBf9odUh195Uws9nkJ9u3R/YCoX2RCLkZwpPfLKSXSVUcl7d+bQdf43v86\nhtc8sUJVQkGypGOtimf5bBGeG/rHrejRQmZ1x+KGO63LPl2+0f8VITzMMfP4\n2rseStRqH1JiGi411eGTYttnvFl+KUTpjavNqSpJPFDFX8Y+46yUlsGqutOc\nIcKEfqPbp9ky7Gi8nhBe/Mhzaf7PAhYQcsiC+U57PrDOn2OmDg1OPOjRVIvs\n05p0XO2EtLh7sVX7SeL04BCCIll7gN9hLmPYezCRkrdqnkPPUnptayWs5UVN\nvOBaD0kerCet/WGYqCJiDXNB2SGfdzPzSrpkbD4UEOpgUQ6EGQxUqXO8gX/e\n1LxH9mDo0vIc+8Dx1YWFPoh3rJoCqDqU8hi9Pkc5ahu/2NISwz2pYUzP7cfv\n8v4uuYLBwTVyAGuxDVxP+2usg3NzDWPh9Wn4w0xRxJfWHA4sSYTo6U5b6FeG\n5DuW\r\n=E7cA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEo4ZxkY1n23A6L3gWj0XoTIcQWFJBPS6iI2k34K3n4aAiAys8uh7SpJs9i+aJbOhAIzEQIvfPBpHX9Bt3r8eyP6rQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km6t3qv9.0_1615584207096_0.5368195046017896"},"_hasShrinkwrap":false},"1.0.0-canary.km7h2c2v.0":{"name":"plumier","version":"1.0.0-canary.km7h2c2v.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km7h2c2v.0+cce6530","@plumier/generic-controller":"1.0.0-canary.km7h2c2v.0+cce6530","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cce6530eea2a9e85375bf848de7e79c9b713bec7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km7h2c2v.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-MsjDP04E9pCQx/eDgn610xkk3R1QwXJ96d3+PG8ffs04xVmiMm7iQgBpdlvph2wGYqY7GY1TirDyTN4GzOY/Yw==","shasum":"c13871cf9b1438f91203c8ff90d93a0cc50211fc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km7h2c2v.0.tgz","fileCount":13,"unpackedSize":27190,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTHkACRA9TVsSAnZWagAAj7kP/AkPoiy9it0Me3KSdh3Q\nBEMLBPFsMM85aTxmrlU6Ctwv/IBpldu/u+vXLHaFrYdZ7dGbxrnjyEg8TT3W\nrkgCAfsNddLgbiks5fgAcMTsX/I7cAbTvwZ0G6WF71oirypPv3cSvGYtbc+Q\nHXzbv7ppGksiFqtclLnpe7fdF62TO/ELmGMeJFTTPDOJ0samCMKoOSFxo7ot\nD8US9SQFys2B4Fm9dcikLsSKne3QDcD9v6Tg8NrbYpyfWsQi3URsXK2Wkvrr\nHh1nNLRshUuO2Pcr964RgaX/SHD8Fy5WP/y4qAaaBAz9GQfl3kNrJVQKRckF\nEgMeIXdCgLV0rI5T7xLysW3q+uGX9HQqn3w0naltI2X1rz9xAiek1BwQxbIY\nb/OaGDgaO63ICzDZCiZEFhDSizF3nko4F+78e4hpL27HRRVr+zpcgUphKyyQ\nMOL9S5/QTx9XA1XMwKjrjK5n/0VG9GActyU/Qv7YxqGtaNXqVJsyb47doSr4\nsjyPza7yGRwutpUEl1v+ezyAQfDsRyaxrB9PmGc+mPoDiTGq6kx5+pzr/BsR\n6R/NnHGGEEqytyHAyInIbS9sxXjEcRg6QSOxA63SmLx/NCc+gFoLvocqRoKO\n5Dyj47IDfcGSDHuNBJb7ZnfNc05S15dsdnF9Rri4HIXk2KsWV7tP3zn6dfor\nFgLk\r\n=Ns2N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEyG6RVJyIz/tDklEui45ndhrFcy68vnyag2JYnLqrGJAiBinuf30Ix0RhXuO/6GPUK63erlK1hPysxIidzS1MfTMg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km7h2c2v.0_1615624447477_0.5616884731007634"},"_hasShrinkwrap":false},"1.0.0-canary.km7n022p.0":{"name":"plumier","version":"1.0.0-canary.km7n022p.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km7n022p.0+07c756b","@plumier/generic-controller":"1.0.0-canary.km7n022p.0+07c756b","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"07c756bd0be426f293c596b011e4f4e513575f4f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km7n022p.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-eDt7qpBDITNoc0s605qmheq2FKepVCwP095KGoulJFWk0TlUlridZnExddgou98yj/G7W3C3M3tPa+eoYpbQLQ==","shasum":"a210f7cd4c6c360b6dd16bf7f6f59e59905a41d5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km7n022p.0.tgz","fileCount":13,"unpackedSize":27190,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTJ/1CRA9TVsSAnZWagAAw2sQAI5jjZaAGRuc8Q7KBSWN\n8DSqUCofoLwNbNXEGHvp3OwP3mQodc5WuFlytsIlC/+lDzn1QK6Lud2QSfAB\n2KAfMjqBug0EYKnrxUFmMl4ucQSjPVJeTWU9reJkdEv4I4YQTuwtJiyuokGw\nFRs+7UKNYFJLru5ZZwtvB/e5D4y0YM6OM/yBYlOQTQL5/vjSCE3XgNRd8s5Z\nKVcZTw8M7+P4DL69IWc3vNfCb6fEWPtAy5TqVcEW7jTc/Du7ZLPqg4hWkcta\n6PmPNvilpWvHAcp843SgQoyMSLL0nZwtUBitkP3bk6rs7ihHthKun0t5PAnG\n4Ak8E04bk5MyJv9MLMyU/bDfpkd7x9H6A+6KE5LJW3iReb8roDfWwvGP0LWw\nrw5PdRJGxo9kRIA5Cja9kIbnHL1hCsbua7vF82p7cr3R5P+5AiQuXedLHRNv\ntDgfWJnk5Tq5rxsJVGqujzsAWaSzeiH95xFOl+aF22TFF8q2HuTV1LCqOwOP\nxtDMuW9S5tVUL+coVzVYvMgwTaTi7Dq4VWK5KM4HLNKC04LehP97yUnR2lUQ\nKYpK9I89UsxYGtAbiqRXE1Qp40XrmZVYJqo5zek60T9YMjQ9ps70jKkt1omk\nwiahxuiDmo1BOggd2Og+RibVdkS2OoqXnzY7qdxea4qcLvvuT/dLpEm5KHL/\nQn3W\r\n=HFx8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCMam1POWP1dI+wkSX4VMZBU2pHIxJgne6H0Qy76Sj5WAIgKaQgBcZun7fCTzZCr6PhkQaqHh7YPXbTv+CKZo/hcbY="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km7n022p.0_1615634420829_0.39567845117251577"},"_hasShrinkwrap":false},"1.0.0-canary.km8aye8n.0":{"name":"plumier","version":"1.0.0-canary.km8aye8n.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km8aye8n.0+8095386","@plumier/generic-controller":"1.0.0-canary.km8aye8n.0+8095386","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"80953865e90d4a00fef3f4df9902d2526cde0019","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km8aye8n.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-taSVWR1v7GJEkkZGLWRoq6d1jSzN/e7/2Xm4EYXscbc+ofUOztxfsYBQsEsDc7OX7bwd98P+156s+PRu36jW8A==","shasum":"493225fcabf5d7c8fa2696d539368ae6a3a6ea9f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km8aye8n.0.tgz","fileCount":13,"unpackedSize":27183,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTT0cCRA9TVsSAnZWagAAzSEP/2MxVzsVx68l3WByqgpV\nYggp7O26jnh0uTHGikthvjCRF8FMSj/EexG+BzDdml8ZeQTru9a5pKKwfkng\nnMbz0xrD2B9pAIFNA5j3PPWx5l9UPjkLtew+JPEqD//cLy6HVGAVy6WOz0ud\nldFg4ODCW077GJwujtbQRjw1dfEfhwM2eL4zFhPZ4YBdkpiowVkjTWbO69ex\nYR8lURNpfAzfEtLQ5Qrp1EkFS3owI4//cqcQfmPH1dfodMULfR2HdKMxgO1L\nRueVfnAwl7pmYXCbSTfAo+K99eka9EbR4yW5SAljLvXw3s06gsabkzI90bE8\nddB5f1+CacgRX4C8I7z7V2YHJpC9TNUPKulfgrGaGIpS2H8k3P+zj5rdR32u\nM/e6K9dIaCz1IrfaUljDryBxmF6P+/MuaE7kug3jfEwHOLCFhymvUIqBHmot\nYvfTCm/kcn2Hi5UuZEoAa7UN3o4Oech3owkqptTrPl75jI3+2WVsVUb2Z3OV\nkqJKeUN+P7g3uMt3asUM6OYv5q34lhIPSQpl5PJpNHHCIQ5L6/mirFJxMPQu\ndD+pBpRZOuEkTE0+pBTWU6nuN3CFr1DDjWc1DKTwbztRPKFBlSMuDKQBW7Pl\nlfDB/LLkYAroBhW9AbTjYcz15xk5uED7keV2RPbxFoeNmIHBk0XUfllylsqE\nFrp3\r\n=eNsC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCU9OqACR0XoLPOv2zLa+U/Wyd6uTeNwb9Y6wGJ/ne9xAIgf8K6DCPJdMvIKAUPiXS3biHwvV7Y3wrVfQlQ4+x3ahc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km8aye8n.0_1615674652062_0.7439800397459573"},"_hasShrinkwrap":false},"1.0.0-canary.km8s1w44.0":{"name":"plumier","version":"1.0.0-canary.km8s1w44.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km8s1w44.0+5e1f794","@plumier/generic-controller":"1.0.0-canary.km8s1w44.0+5e1f794","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5e1f7942ed904f94a5537d187ea04154a6a99bcc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km8s1w44.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-Y1pfTS5k+vAlCDxEjTxcZUdplBTAwMrbRpZeQruepQ3108ssRQd03qHom5M5plIX96fNh8Uv1Y6P4HgdX7h09w==","shasum":"d9db71e816b06a4367d29838ef6320951c17d03d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km8s1w44.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTa1HCRA9TVsSAnZWagAAWmQP/3kNocfoM2pRS5VcQYwq\nEYfHBAKI8gRHsq2eHon4DX2L7CX5WfEdGwV7g3k6ei/8mmV6gfZ8spvwIJQ8\njpccA47PEZxStfppkFBwd003XKBE3uG33adqlvLQ0lpwQ96EkIA334mKod7f\nKW+F4alwYBopHLf1gMHeNhB9tfwGwdSgD4kZR2uhJoC8LfAI9U9KeM3rtkdf\n0aoCzMSDGlvODAXny2BN2kpPgZhSTmYaOxwT4TZRpVQ80ub1wJyq+wiN5Gjw\n2Ob+ASlFAu/GREyYVOfXqR77nbD18ZBDoutexJE6gZ9Xpo6qf1P1XSI7A09S\n6xb7RvoPpfUA0nmGHPQKMmKMs9P/vLH0Yw4r2vPdDv2PfPcngMFote9AtF8U\nOvhGNC0mtPc9gXFubojB/vi78xG5ExfHMhTb44BSL+gMn4xWLjzaIHLVnMQo\nW3vb953WnDyJTT0BJtZz6/x+4QgGsRn0Z41a4I+631AL6DtExyo+grBxS807\nug+FbdFqm5t7l8IU3d/3JkW8vialVc8dUOHj0kIrDQWnLrNydC1Gxh9RNS6E\npttn2cQ+FCcVGKKrFrdYcxW9Z4QU8ZTZPo6JWxtsxg1ulCL5mHdREcd++bEH\nunmuIhFoBvrQxdJm9AEJ35SJS3rkiSyyyBl5498h8drJbQ2KZcnhhrGkQIKE\nb2rz\r\n=F1Iy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCo5I7PWT9dOQRL0lGOOn70VUpOVkg3XgnQGk3Zj7gXdwIgMgpGKwxnXyGUKG6FMYaupK4kW4XK3DpD4Cbh73UUyNE="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km8s1w44.0_1615703366868_0.7880469897889599"},"_hasShrinkwrap":false},"1.0.0-rc.9":{"name":"plumier","version":"1.0.0-rc.9","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.9","@plumier/generic-controller":"1.0.0-rc.9","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"42e7c8095f8bb440e94620dc18ebeb90733b07cb","_id":"plumier@1.0.0-rc.9","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (darwin)","dist":{"integrity":"sha512-oMc3YegNCsVsfTpJVgY5N8Kw742E+8Oyu8C1IEvLVi7ZaGf1SyZpFOHG6t/rMQDALcnSKaLmEYPKSqxnL4AYyQ==","shasum":"6086f44e066ade778d13635c7dd9ea8d1f6eab3a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.9.tgz","fileCount":13,"unpackedSize":27515,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTa2QCRA9TVsSAnZWagAANa8P/iiixIgFwg1SgRtndIcT\nR6xG9tepktT4A/TozIlhQuFBMO+z4wTwsSA0TBq49HOzr3y7i7zFM9EQecsj\n44R1cBiPVSDO9eqYSAZBFqQInQ6CJjPZLd9EWC3uQLoAh/ofHHo7KUNddCiN\nzyn4fgrNar/6abAbSkV2eEZI5ZgKwRi/mxZeAYXBPfKAtDWQf6dAi1rGXgXO\nq9KceAdejKRXr1RgmU7e1BeVahT237e1oHDdOrCoc0bV23eD13zs79IHA13U\nHHc8flEcdS5ERASaIz2ZzNJsdprnRw+HAvjx+v28+6OOMJRgDlNMUMZNI8Fo\ntR8RfrmsaTtY2unBPkAPGPVcIQX57Y0IcSb9vW3hscQfaKqc0tj/ctD5McDa\nXeyExnIxC0DasdoCD63WfTlR0HT42oTwy0aXM+mK1j7TSupZqC9XoMl8TQ8f\nVMgFNO8UolvqbvY4B/SBNEODkopEcw1IW8o6uIFFoqoOSicdR4RbOpPCSKYP\neJHdpznRvhNN+Mqb5PiqD32iy+g4SzexVQxiHKmZlY0jubCaQHOo274vqGav\nDAFi6wNJ7hHIyufm7IG7E7nk1YJaKSLPXwJjseVMmpWS/vmsejHt2ERBIn0e\n1BQDDrjH/7/MU7lOn4sfaWSipfCQ78sp8uLITeMO00Ut9R/YI5UJTvV765C8\nKKFu\r\n=HU7Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH9oow8VdVtowdvo2A2NiB/DyKsExpmeVIXuO8yEy6YzAiEAnIwmwlY6rycS+GdLIVS9pHi9/kB7O+LfHELk7X25H9w="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.9_1615703440109_0.8133353867408553"},"_hasShrinkwrap":false},"1.0.0-canary.km8s5ws4.0":{"name":"plumier","version":"1.0.0-canary.km8s5ws4.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km8s5ws4.0+b96bf68","@plumier/generic-controller":"1.0.0-canary.km8s5ws4.0+b96bf68","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b96bf68902d549d757bb217e9c4844608fde38b7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km8s5ws4.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-6odFZI59T+yMDreV1rdhAOrPzf5trqfsGigk2T/VgLR0QSxeduiUjUPboHnAGQX4zvhvcSzPqDZsE7ObKg+6CA==","shasum":"1ede7d80e521dbe5f2a5009a245b53f807ff422f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km8s5ws4.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTa4CCRA9TVsSAnZWagAAs3QP/RFTYJVjRC17RPYur1gX\nsvAv8dKBZCpC9G0oLfITHJGbVvS/PYMfSgPTyeQDrO1n9lAxlk6LqS+6Smkm\naWp4BdzSKfVnenka4dStSool+yX/ifUYynS8n5Wb6+w6MUp7l5ORQa2UK3sw\nBLQcvk0ZzhID8Jrxd8YhbI1YovztjrQU25uJnPbNsw65M1xzduFKaDjSw6qg\n1FIC2CAJzi61r0k7F+7zfAy+xadv9ed7c3LrSgttIUNB9MaBPk/usMOCPbz7\n7x1VzrtejYm/I4sU4gjW0YmjDdqXESf8sc+voMSwJaXMvsY6VrOlbaU5gNCU\nyfHsa+68FI3UgQNYZLPY1KZ3TyCFEAQ+Bf+L1RDa8UbT6cSogP+YgI5NVlZU\ntzK18vV2smHqhQyH095IAwkPm3rpf8Yt30nEsTpAizWQa9YI/dyBYMtifAhg\no71NsWmkS5OPRr0JHKN4myqU6smVB7PIhnXbbmh1SMnz/AffKFr4uI7Znk7S\n1oKrpqtokroP5ivFASXPVdeOm7UjxZirYZAJdw9emeTaskc6+S/ezyIutu8u\nj7LAd86LWqRCl5rApXqOLIZudXNywTdpKXuft7TbSJCdoteiZd20qtRoIKxz\nOvOlCVijREfYTLqb85Q5A6lalktzo06vwWmHNH1qlim1NdsimcDV2L7Gdvkn\nlVO0\r\n=PIQM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMGdOdnwyBBPCzo9WJHw++MqfK00+ajBGlXg+0ps2xZAIhAM3ShT26TXQfkEbzYTnvHCq5vlzYTpfB7IwZl9Su0fgE"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km8s5ws4.0_1615703554067_0.15646346098552044"},"_hasShrinkwrap":false},"1.0.0-canary.km8s9uyx.0":{"name":"plumier","version":"1.0.0-canary.km8s9uyx.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km8s9uyx.0+42e7c80","@plumier/generic-controller":"1.0.0-canary.km8s9uyx.0+42e7c80","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"42e7c8095f8bb440e94620dc18ebeb90733b07cb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km8s9uyx.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-Q5VkVFPxBq4FvesZ4raWe5+idrsjqIYPgBhel9qzcT2S0dIwyCkf5VzGQ6SIDJHeM/yP2lpyGKNJeSsTQXtM9Q==","shasum":"f98e5b08af288f91b7beec8f57ef8049a30660f6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km8s9uyx.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTa67CRA9TVsSAnZWagAANSUQAIDdGoVrIdThEgPhDYmI\n5Hb22lsHZ955PI9j4thUK7OI8p+0p192Z0b311w3FPBuDtDsNTOTfsPO9ROx\niG1K4ij7vHmvHZsF0TYC8W2jpdl2nQkSW5Q5ppoy5f7tygIXC/IBgFdPVgCa\nrIIy1jn00tk6ZBpS1xsyokPVbMGxr2fArJsFDneB5PgVs20zmAGQnHOq+xQa\nuo/07/5DVD9JDbv83G1ZhGv50q3kBZL40CLjo1kYNVDLBOA2RbW4b4b24eIL\n7tp9SloIjjgv9nvI7bm36jXp+WACxn6tepYXYR8j/c14ywxTOEezRHFgx6XZ\nbz5m+Lvr2yu0/wAn1NZ3Ka/OzLoaPMKlqnxL2yZu79zkrrH6MYKyBn3ojIgx\nXM7gMewhHcaZRr+JJCsaA3ceFUfrQzWDAZnZ2F79vnkGfXtNKB+U8TPfhRxE\nIwVU6RB5Hy1wwsdbxqu6xtiFnE203W0k0yc++Opym4JwtRnRvppT3iNBNhjh\ndqVku9slhMYun3Nz8xweUB0VR4r41z1DRwVCiFwfCRSKo00AiIFxjZ8+4KUe\neJy3MqIYWsvF5W/cDd4I3bFuuTD5BQ3Mr2wdvZ/nqFmu/+twAIlH5go0JBES\nDIyaKyogMam4XmtTwgGyKHPHZdlD4qgnAD+AiEk+xbdCAIueNFIMikbBTMPi\nmx/j\r\n=ct9O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFFZ3IW+14KBNzlV9mlOT7WOta7RgGwPYuPTmlRNs+v0AiBKrSgGDmmNaMajVJCPYLHhQjFPFjB0X5Xs6gzu4NNvtw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km8s9uyx.0_1615703738936_0.8003125880164117"},"_hasShrinkwrap":false},"1.0.0-canary.km8sfd2k.0":{"name":"plumier","version":"1.0.0-canary.km8sfd2k.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km8sfd2k.0+2439a0f","@plumier/generic-controller":"1.0.0-canary.km8sfd2k.0+2439a0f","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2439a0feebed3053d63795ea88b312a1f19fca3b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km8sfd2k.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-VOXKYeF8DVL52AOcbYqN/dRRVo2gUEvoi9tCZ0D9M+yvlWhEJ/qAlQms27zFO4o9MsDFY3iGM53Jqa2NrrC+mQ==","shasum":"3d4c4dc8dce897f802ba167fa272f9539f339ece","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km8sfd2k.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTa+6CRA9TVsSAnZWagAACO0P/3Tcvh6UWgqa4q0t2DzA\nKo5rs0/uAf21cDQly/5WvKPCrudiNz1YsV4MXk1zgzNkMNaasjIO0tXiZaUM\nVr7DpDqt7UGE1sX9+mIEuMJ5tMLYV1qyml21Yi4RliuixNKcZX5+z0RMdmfT\n9tK+FN19rFewGrDOAuEkdDtCLL8KiKDbmgn+OKOY9EfcLoBT8RbkSmdFhbWS\n5UKpkMfpJpc6htb7GMwmIJR7+jz1H/nEkFNt24dWvh55EU0BAeR9VIlKmwFx\n8aUv6vAtDkt0rTKUWySDt7xldTFC1OnefFh25/CKmtEoeMBOdCKuyi8S/51O\ncZnmcv0Fost4srFbN9K3RzBOJ5I232VdeRU/fu+2l6f3yeN9QRHT44B0uvlG\nAU6HQIptVvItRcYijxnOYr3PQ0QPixXw/1DqyXj8hvGNwKzeYTqJrSpbGAbr\nZEI0Lw+7ExEEGHs2RFPOjfAjE+Ju46SLJUTjxu2l9ElLzpFjUyd+VMLsv+ig\ngswVu0WrXfQTl789gLsQnYaenfgnRsgfoyMmHct55sO62z09sMI/pLTeu9bN\nlnJyRWZf6h7VZio7Xr7pIsSKD/HHwiDmdH7nqh0/Nr6i0z20n9x2nGTIvU1O\nr4ahZsTtt5Zj+kpswhrDC8P5ZqLiFaM8NTpKtL/2OuPBlXsqcTt2zeKbg5GB\ni9jW\r\n=XMEK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA3fRHDYzB4smFTykn1wRLl8WEWaPtgQR3SkJP5RVO+2AiAaxktBwIWrTKEYUDWRiuad5SfutQH/iZLCq/k6m/kIeg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km8sfd2k.0_1615703994335_0.30157092848527567"},"_hasShrinkwrap":false},"1.0.0-canary.km8w78rt.0":{"name":"plumier","version":"1.0.0-canary.km8w78rt.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km8w78rt.0+3966f75","@plumier/generic-controller":"1.0.0-canary.km8w78rt.0+3966f75","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3966f75d04bc86157ebd769ec5b5528b49f8bfb4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km8w78rt.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-zPEL1T+0IfM8XW+AuGeAjofeO6Szd4aV/Epx7YRuEO1BmeYiJoFs9B/5z3kGobh2uPCgm+xqtw2YIjjcpAZ/zg==","shasum":"d51ed282c99294528c1590bd6998384a027497e5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km8w78rt.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTch/CRA9TVsSAnZWagAAHDoP/3BntBLq9hsyDBcjpn2H\nsw2hopxY8MW85PskNBl2qAClgDIm3HcQ4nH7VgolMnv3rNIUUOCTDDQfGVg1\n5AaHHNiYTYvhPjaj7ez9jfpmLiiviZHZop91wLkvkFfa60SC6/3uDTkxNajv\nu3X4uj5zGqa7OQXtBK9EcRcrXHiij4YLJTTp5eCazKn3Mkw4/KAdtXX4W7fm\ndkxsIiFzdNxVif0IVRth7OT1N/BBDyUvAUe8rtQ001uOGPkCW+7Y6iNCrQkS\n532EcTAzkHl2X6Lp3+6ALR4U0S0NoPxfmrZ9BCPro1mH7NXtdYfeJ3U5S4LQ\niv9h0lkHMgtWdWAmYsFVHwdRen4dHqMRq/HkVVGUcoLnPVse19U2vBGt5cim\ns+mk0Y6kHeLUrqbgQFuwVZP41hfoUjX19KmMkAkFyExMItHbc3aQxcaVuES6\nXLfe4I7ktXsNHfBtwPmjGp/GeahntigL5g74hcaTcbeuREty5wD5/4ZWnz/1\nWsLdnK4fpG6DiqvL4uXtiMx1NvZandQYwek8eeuGEf3djK3FURs+kG9erv+c\nrZ4tVyUruS5fWIuQTuTmLLUiLaqAwkSfOXEwdP9Rrk6Lex04zFWsq+SjIpGj\nO/jAhehraPJ/pIyMyuOEKnywTRWMEzTethSmB5uXxeUnh0p/aotviqW0TvCK\nuG2f\r\n=aa0d\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAl0v+mPqGNn4t/0zFlorwT03qfUewjtsrApaY+KMJRwAiEA2uqf+mSHzo9T5qlM97bC8CQNzZEoMvkJjSJxAaztDqE="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km8w78rt.0_1615710335434_0.03676232657010714"},"_hasShrinkwrap":false},"1.0.0-canary.km928moe.0":{"name":"plumier","version":"1.0.0-canary.km928moe.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.km928moe.0+d025cd7","@plumier/generic-controller":"1.0.0-canary.km928moe.0+d025cd7","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"d025cd782a1d133586d8648a683d2376c0cca026","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.km928moe.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-bQeTs17SNP/lxk3nzvmGpoufI686P3dF8japXYfTxE2g2vJENAJSCB4fGGL2Wz9PMBwEz7/ngNtg5y11FAynFQ==","shasum":"32f68f341f6513ca872fa4bfd47facd234e9c805","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.km928moe.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTfAiCRA9TVsSAnZWagAAfAUP/2ih1LEWaM9ElXfVnlan\nPe+RdcULFJnIQrf0lED0Sq9rB4iIEsmnzSo7z69jNGWtX0quO1DpBDMeaLtM\nXxIOlXyL+uVNaqWJiZVHX3HsW1JBcWf4GdtHHl2mKla9xVDK0A/YIT2E0ZI3\nCXkJthCAWNoBvcuqJuxe7HFg6AN8Ej3MIippJOJFSUqXH0AKgHblX5fGEnu2\nwOZjeENZ9i3M5OuvHTsST+UTi/FishggbZ7/BqwYr0Xy2WuVbpZxusoorP83\nIKmKlrQKDekbS603stIHGkHbDYeLIYo66cwVxt83vjP35TwoSnAsyjsylf4P\npYvDI2V4tcA6P4KkcyQLEf3r7RxPUDdQpE1+sPyWBCiH/N60O0KACBJD9f6J\nQNNjO5kF0yfXL7STRJ33RpvFDJS6sFyctdZ9JgCVEkqKBAgHGGNrt/TKwhBq\nuGbvOvInHQdVgDbcWY5QWoxRNKeojG+Ps08fDfm2LbHy3gQEcnzKnOkMsHAQ\nV1LURUnLUnx0b5maqS6Ozm0epW9NhsWA24d4Ab5q6hgzzgQZ68w+dBvrUl7+\nHcK4FmVeSgL9lxwVYJQMDFCPkNnyT4fHcfrTkGj2xcIppNIfB3JZzC33c66V\nK/HtbLQT8Qd2gD8ddIXyL4fkVknkuPKeHVlP/IBsddtYukRIoHA4CEh1eOtl\noCpx\r\n=p/GV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCtunZWkWB4RMZ+K2YliLFWNe8xj3OcdxHWfrPdv+GgfAIgIxQeht4e61siSXH2P+2WRRw2LyeQzTL/pV9qI2SIvRM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.km928moe.0_1615720481718_0.1533465035118875"},"_hasShrinkwrap":false},"1.0.0-canary.kmbarc7y.0":{"name":"plumier","version":"1.0.0-canary.kmbarc7y.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmbarc7y.0+cf07283","@plumier/generic-controller":"1.0.0-canary.kmbarc7y.0+cf07283","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cf07283c011c25bc04ee61e1f89b3be0a5b0db3a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmbarc7y.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-NjV4slirkS5+cUKbtc+0YsDUyRHmQo+0xgvD/8oslKmRDqq5KVH6MdUGn38QgiCsp//c726LmY3uMMqtMSSuFg==","shasum":"01d4c80768ec948cfe727ba9478b8d6a46eb0010","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmbarc7y.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUAB0CRA9TVsSAnZWagAAIkcQAKLKHT9dnDMQw9+JQwoI\nOUScLnEkvvxus/m4/Incc/KKc6GT5p/7BQDth/SKzloEH3wvFWLtM7qZeYwb\nTVFI74d0KgbM3VVyEIWwayr5LsPTsZb0Jkf2T3/NW3R6qYSnb550XjMZMcYI\nbTnWb7E/3bNjhCA7gVQeQA0pmG1B5qZQmRvuvi6Ou9h4XTgnf6A9U0IboE6T\nj2DIRF7+awgX5upEztIjQezuLpqilWo1IR/9lEUI6pR2p8VhfGfacUBPw5Wq\nzXeJuAkbQfixBsJxrN0N5KrYmx3+RdEkvEzXywApI1FotW5VNX7YSa3ELSVa\nwFHeZHFbO4CegFZezWpt56Tl3cxLF1nS4eko88s6z7sVIDn7IinofLMbVfT/\npfGWAVhXisGbnJuJbacYpBvrfJ8txvUzJsQS8FNcjPPgX9QRKQD5nCSL2WWh\n0s50Q1+TRuj+TTiFDJ3t+oYroY7hXgCUAhJO9+Hipr7djr8nOwf7YO0P9mvy\nmfCDyZjZdkD9M/upRBrIjb49fhlIJIOuzC31964JywgOL+ORbVJ9b80phQJr\nv3a/KfJU8TFy3UTPkWEf9U7AsfjKogznPrPWWshI3MH5xPLUC2CRhM+o3XbE\n2IX7nP/exgjYHEko8XuaY+GVXN57kyQJPTzSxvyy/sFymYAi6ie8i42Qxya9\nHadV\r\n=F31h\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDG0UQ72UrRyF7IYl7POi0nL+SPsG1yvcoqOvkjxqk0TQIgXtDuYX25lMETXCefD4GjcaY+ZDHl+iwN3+KkKhee7qY="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmbarc7y.0_1615855731882_0.40027509931691196"},"_hasShrinkwrap":false},"1.0.0-canary.kmbk3p6o.0":{"name":"plumier","version":"1.0.0-canary.kmbk3p6o.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmbk3p6o.0+36c1c37","@plumier/generic-controller":"1.0.0-canary.kmbk3p6o.0+36c1c37","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"36c1c3756f8309d051ed3f2403b4e38a9df2132c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmbk3p6o.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-QHpZWyKobDjF94psXwfHdMIcbPDRRKr/OLL0N7S9wtvcbYee+W9oQR6zgQuto9cBJVQgwiBczu10Ue3ZTIt7qg==","shasum":"f9912a2b0e34a6f69833d7ef17cf13c6adc52105","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmbk3p6o.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUD26CRA9TVsSAnZWagAAli4P+wZ6ewsTFULbu3izBZ/i\nT/6xKQ7T4OAr0zShk/4s3NP+lIQV3JSxFcZ6+wLM3aXT9vta/zylHJMpTFEK\n6Igdq2O1rk+zNg1doCjFxB5BUz7B2xKLWq+IvHOkhrgIkhTK5bM/yR7Wq4SI\na6fkEtqhXjp7o0jMBw3h05aG4fx0DTssEUetfvlCoSjYD5SRrJsA7kp3BLHN\nwK9DuDJtAH3HBuVOXfkWIASLnQ70r2bUGPA3x2NraFyCJqHnPejDXALTVETO\n+ZEfBEK4NqdfmrHfNVVHIw3RiTbOjB7SMFO57UGOyRD2xAdvDJnbCfukwU7r\npkLzrq7Ecz+L8IirD6/s5hQLtd7Pfpnq/wfsNzqc+N/rFtr+sHCoz2+qFKQr\niIs84waMEAo/8SQlGpbfFEHKx3Ia/b94I1ZPXd3/quunhbGGzfqgQz6xtlZv\nJI2JzrPRj6RoioSrVGUt2kUvnW3NXG1cossp1j1eCTAtSaQQrXGgF35pDUw1\nxSQH8QSM/MTx0aSRXWoowj4n6XQ78J2LIV+08oeT/QLENIl56WTotnlwVvN6\no5mnLOJQKUiK3too+at8mpcJC7qDbC2fuIltiooQ09MYzvR5wZRlmPtphJPq\nuOYe6Foy+wDhkOig2/NTP6bE8uDuZkUAK4PGqzYGkthRZA2coSm1DLQTsbx4\nQHZ7\r\n=mPBS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFJPkjoAjsYevRdddkgH1NEFswIPKs0EjXcjTDG//7TlAiBgXr4KsRwx8vKR0aPz8EUnoU17+/qKlUAcvCBTZCqrMw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmbk3p6o.0_1615871418175_0.09719208790394185"},"_hasShrinkwrap":false},"1.0.0-canary.kmc224qy.0":{"name":"plumier","version":"1.0.0-canary.kmc224qy.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmc224qy.0+1fe4eab","@plumier/generic-controller":"1.0.0-canary.kmc224qy.0+1fe4eab","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1fe4eab82f7d30667320ca02dcbd05f1b538bd92","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmc224qy.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-3MbIRiyp2RhMC9RTOMBJKpcM6HRQDPOcJK79zW9Qa+jHfWDjrKtLOrWqVSDNL2YjdBIBn/230ZxOHKLHqSKmjg==","shasum":"e343ceffd5483bbff2e9c72be41439d90dd55fab","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmc224qy.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgULOrCRA9TVsSAnZWagAANbYP/jhd+Mi+AFkiBJBLoOlB\n1EcDfFPEEfDh/wSkadWdmYZyw8eRRuuPs5PSEY69yF2Wg2mhvSd5511NubfE\nBbRrwer9tRmxX1Rk4y+ThWS4UDLJk2l70j8IamYNw/78gcODOewiWbHKqA7I\nKBsWvj7FXkoEDYZCv6p7eBWhu8YmUn+Cf9mg/Mf5k/niZElzxpnjvfbVxRaV\nlKHSzbAJasv7Zh9hXOOkwcyqKo+rVCJE6ue1lAMuj/+3wZox2zZ7qV2C24wG\ndx99sjbGjvWmf9Fzx1ANGBr888MBprIkGzGXRmP3oik/x1G9XFdiSylwcaAC\nohWii/SqfvAS8RZnGQCgGekYGYmDWz5KKHT9wDCy9Y/pD2GL3bfkmNoyTH6n\n2VG0fnvE01BGEXrFeVsvIFWQEOL7d8CIGzv/oWCAeMAEp2pOa0lbY4aGLtew\nXFnMNFeYFyFucDEgNU7w5tFNDkhbX45LM2mNI0NhMg73Wn2Sb/jKEzGw26nb\n9jQPfpQmashlIV1wtrNS+nx5/Eajc2zaQ2VxNrZTn+4WnnQgvd6F+KPx4hc8\nCjvnH10idD/AN8fBCDhVoTwfaE6wrkBWfjA7GLGHMHTcsvjTAX6IWJ3egwHz\nNdY0uyR+weO34hH3OT1HPmT6clX+lZ9cCmqUE455Grl1mzD6c8V3xHIfeXKD\nY3oy\r\n=k9tB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDJTuncyQ82/qtO13zw2g2/5E0h/4wUFX0BlYybgBQQsAiEA/fFWT5UfFu+ruYS/AeGvhuY4gE3eVIuK7opEjcqzJMQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmc224qy.0_1615901611402_0.6885857563083895"},"_hasShrinkwrap":false},"1.0.0-canary.kmcgxz7a.0":{"name":"plumier","version":"1.0.0-canary.kmcgxz7a.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmcgxz7a.0+0009082","@plumier/generic-controller":"1.0.0-canary.kmcgxz7a.0+0009082","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"000908233859618d98c5c492fb3d19e111c63507","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmcgxz7a.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-lTXVbxzYpQPb72jYXZAH61VK9VjTlqASv2pzNqVNbJ2RhIatf24EiNXBZBqXhvFxNM/84G/wi2u/GhcFXu2IHg==","shasum":"c4e709d7dc961ade6ca87014a183bb12c7e61038","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmcgxz7a.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgURUwCRA9TVsSAnZWagAAsGMP/0CnLResnZRpFeFdtNSA\nvstPpIL7va3qdwHDTZzw8kU/VjUKmLq9/mmaaPGkUvgsiFRd/J++w7p9sR0s\n0lp10Ru1nowJ+pXSsTETWx8yfkTed5EKvZR0EogB3wDWbVM5HDYgJeG1fDQu\nnHArSx/yfQq3jSD8JzQML/2YfpuvaBOsidfWkCzdiuB1bK835bJCjoVQhuqI\n/MrKFFJidu/g4yaloDhWwDb+UDEPOA6ZEhP+aQ37FNpCNI7TNOAUk2Eb+nhj\nQT/q0VyvUIXCHIXGzywAmwxbQ7ACf0MWtsMpz+d2N7mVaca/xy08kT1hQXav\nKEIsIse+Rc15dGJVgS5eZ4X3vQA3E93pucTDGMu37uYgCt3jO4baimCpqe60\neCsPItV8+jjC9594q7vfZbFwNiPNgJr3wx1hcD5IZWAwIoPvBW7uL3RqH/uk\nZ49A6m5m1xzECLg3I4fcp9XRLMZHbQqq9YgQ/X0S0gL5KigX7GrbMQ9jTqH2\noKdsCHXKb2DLDvoIYrtlu+DtPgxmwUloaxD+/d49OmErvZJB6ziwIM1ZP/7p\nOAggBc+U9l7M6J5HGy0MHZvZm0nUhhBmv0J7ukgTtiS+oFHqSuhBDby1wuvi\nQWRxNpGkFOCa3Vqgd/AbXI/ePqdsr2P5wdlumEcakbBljipcpohq88fJxi3x\nmEl0\r\n=Z5ys\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC771CBmoDHumm3r308N9xZGn7Wzn1YshEZH+zNyQ5ZEAIgYtSqEpHQe8yIkU3k8VXP58mFh51KP5gwIK2cmEbwfks="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmcgxz7a.0_1615926575875_0.40702314100441805"},"_hasShrinkwrap":false},"1.0.0-canary.kmcw0m2i.0":{"name":"plumier","version":"1.0.0-canary.kmcw0m2i.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmcw0m2i.0+5c23d60","@plumier/generic-controller":"1.0.0-canary.kmcw0m2i.0+5c23d60","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5c23d60773097e2edf9c09b89261563779710975","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmcw0m2i.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-uLBgFpl7NFf9f/RCooRebANLdQH+j3ZYVdVQcooy5D0R1LprLq9suLPGNwjHjBJdSqZwgXXilILPD3qLIsNMNg==","shasum":"283f60d35ee9e47d77247e85ae418cb6a348c5f4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmcw0m2i.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUXgUCRA9TVsSAnZWagAAuqAP/iYevkclBpjIoiXvrT8U\nHsiLCHj//4e8ztzx35o4H12xhKeDTezHmFLXPSZLX9PEtZjw2cc2cPMmndde\nPBnbX5Kqm0ILhdwI6bdtiNSSHMdjHMWeYJfKbKpq95uxQJlmDhmrYMI5xfc0\nvzDEtfgXQ6+T5GwmeV6mYskXPKtyoOzlhIPEd59f46+mnwheA8br92lFCVB+\ncl7E5djWrSmkQVX95uTp0g9U1zrIK3Q1HuKF/MnRFNoPaC7iwk5MRZVqXQjh\nzWRCRaF8a19wqsTX+aOuu9WCmXKG2BcAs3rQyBSjXPnpWjlxyBAPdDBq8CfB\nNdmJlzXKM6W3n0gpjf/b3XxVkVGjnAindlxwOyXGl4n4ZO/um6KGyTURa1IS\nrG7RuvQq3/J1blgY3+g7i18aPFXAAiiyciFRS/yxPtpbTLBO9YmavR1Ze7zY\nNS8AbFKVL2mfE+1K8+nacnxfK49VHCvzleupipxN5Ek2VxhXJw+zBpoVNw2H\nLSk0HwlAIxn+7ee3mJs1374pvXtEtbCNyPyQ5RzP8BEtPJvUDr3doU0A++Q3\nWgxlkU7Iio+xUJl54nWZkwFAECycNoGKQuJr6NmR/oLj+2i7bd5oI3afyuoL\ngt1wix3pKtDKOwBPo78MXP/e4iu6gWJ1ruAcQe4nxY8sb0yo5PZPe9jeCiIO\nHZKg\r\n=m1zK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICzJojimFCeubucQ2Sw6yxAZ58uR88bhNa1G+dVrinx/AiEAr1DD+Rm+0sAJzYgvltbAijvrvrJg/SgztiO9PTldoRQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmcw0m2i.0_1615951892011_0.9847794600679536"},"_hasShrinkwrap":false},"1.0.0-canary.kmcwi1b3.0":{"name":"plumier","version":"1.0.0-canary.kmcwi1b3.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmcwi1b3.0+8764dd8","@plumier/generic-controller":"1.0.0-canary.kmcwi1b3.0+8764dd8","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8764dd8c332392b1f87d55ccc5243c5fd98f462c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmcwi1b3.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-/cPAfhP/TEdZhdXeKoTDMRrodFOZp+ThBnHOe7gfQcqfy+tmd5iGGJrbr1u9FAp4PgCYCl3QLukJmY5ducorZQ==","shasum":"70a47bf790e8abcad988cb1171bb4c7134ab0040","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmcwi1b3.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUXtBCRA9TVsSAnZWagAAVV4P/2KR8Yuf4jxyy8EEpzH7\n+6cqDa6vxu5S/ZHfWcGFCsDKcFsGVldAPLGpDdXhjWmwWNwlPjRE6AxwMyYv\nQWbmlUgIMSau27+BiYBVZTF1LFLSakQRi2EBnx26VVgCC/bQ/YlXZT15RyLI\nGNnRpAK3lJuTrxcqrE3ZjudBzfIs2m/6XvxKaUOZ2HQ0cgDKGcPybZzlJHV6\nfxfw3tMwxMIEVKy3Xn/P73V/d0VThFCQDbBgtPNdLJ+8aoutI6TDDfeqngrp\nUMTUCY90ptNXHWsb1TpafwlDXKjF8sntKqFIARYasw1Kcz1xf7mZ2+aAHpMP\nkm5bsK6W4kvuHouZf1L+o0hQdjzBvJb92tvf0X9FERc7rWgylraxobqbbPxs\nCltBFYsCJ5hy0AoWgHBUOhpO1KVz0ERkVGLRjl1f7yH+I6wY505qkLIreF/l\n3LUtieyFGhAfTbID5dzbODTv90WHr06b6NJo5G/iae1H1TuvvNUbPBP1rclg\n21k68pkq2ZdwzbNF1dIWFKuPY8l7GoBtEi6gjFWKpEwaMHmkkHlZZJbjc2DN\naU5dUUIVByPy5/eIq8EVjjupkAsmb/KLecRj3591a5Rag8e5HPn2MF7xt8lk\nc0Wpcw9q5gt7wVRNuID2yxTyOVEz4v/1nsoBENAH6zG97S+cEfJcT6kAklyb\n0LkY\r\n=9ASw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDUu37WO5OmCSWpFmIn08Kxy0fbPcaiNEr1l/3/RytMUwIgXpdh+esB+OXCOsfGH45yTdd/6MIbw8ACyr/HHp5sGr4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmcwi1b3.0_1615952705392_0.40589364071695866"},"_hasShrinkwrap":false},"1.0.0-canary.kmdwmjgv.0":{"name":"plumier","version":"1.0.0-canary.kmdwmjgv.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmdwmjgv.0+7081fb0","@plumier/generic-controller":"1.0.0-canary.kmdwmjgv.0+7081fb0","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7081fb09190f1ed381b8e040d8d5202f9f9ac528","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmdwmjgv.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-6nupmXq3t//bgVAK8PXJ2yGEVz8MuYxndRGt8M34fF2FTMk3p7l2yZ2d0ai5qWWEl2hPNd8SBW9Y6G3N2fsiwg==","shasum":"53f65b4b3e24c33259b2ed483832e91a701f7989","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmdwmjgv.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUmhLCRA9TVsSAnZWagAAbEYP/3CeZBw8sMJMERoGbvX5\ndDaGbEI+N3whlTLjEUBD767qS6GXVpm/CJG+a1ylqSfkynuWt7OEmZ7eEsxQ\n1hjuQjY6nIxWgIZL5r4SZ0FdK9dY8aRBEyurXHVogM6lM3V0iiMrDAOZFce+\nDMIovRHoUZftr323PUpetOqkUZBjIcHwh7XkWeEMac+ec2TTqnwSt1fl1V65\n3qlM2ptca30JGwZ26Pj+rstclpRac56QWO09iZHuZ0EX+Vdl3zzdf8CvmQ2B\nOyL7u4h97Gf9AfILxsb9rgq12NJGQgid8rZ2iUS51EoLH6dOS9tvlgrZsIZJ\nnA5HRRTx5jHcA58IYqgz8xpayi2iAbNuB6r2hLWbovCHznURgtJrOdFuRqrj\nkVztrPGtLYO1SZsbtcsmcVST6lZ6x1fGP7zGQPMqxRr0hAwFZ4Y/QPX+E6FV\nIL/XpkXUz9d55PUmOLmAoTjnQXVGKmkC5oFQlJf79+BsqUhLAHvqJuWi2vCR\ngkiELEJgxFBxTbnHWsERwgWU3y+903kYi7spUEcelVwjyhr+ZRdrZ6xf62hp\n94DWtGq7DA34C3DniynxMmxUkTkRszd4zlWMQoXFPz0kNgBXwenenuAnKR+l\notFKx+NHp0wievYAAUJ69aI07nHp6xW2wEqO/bGSJaRA0bqLO3IenaDAM4kM\n11hn\r\n=TCvR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHMdE2dImrOlhXsVsWYyMvc00Ieu/7QLslhT+hZ2OfhAAiB/HJw2sy71N39tzaW22IhpjGM0z+/kIYwhPAI1Rlue/A=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmdwmjgv.0_1616013386628_0.04702028560936311"},"_hasShrinkwrap":false},"1.0.0-canary.kmhht7ke.0":{"name":"plumier","version":"1.0.0-canary.kmhht7ke.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmhht7ke.0+88640f2","@plumier/generic-controller":"1.0.0-canary.kmhht7ke.0+88640f2","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"88640f2d1af33a9fd85629103d3b04ec1dc6184f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmhht7ke.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-/trO24XoiTAsQHfLgZb601vjc7xpxfW2bcbsI/tB+C9fWvMuH3garAGPXacm+DN8s+CvTqpff7ucYr84M+095Q==","shasum":"733dc3c4d680ed66e8b4e224f24510c77780d9e6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmhht7ke.0.tgz","fileCount":13,"unpackedSize":28494,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVbfbCRA9TVsSAnZWagAAhCcP/22BTR3HKwRmhqZdJexx\nd6OBsRLLpO1x0QJ6MsKqLoUI6BXkYYtb7cr8iHn2WFEbtvLVWbGvutVJPh4P\nEpfxdUG9LrT2H6tHKtTLa94h7oV6SRUFfW3vA07vm4qHdCnLiG5BDWOjZMsv\nCKHlXzHY7ygs4+5EdDSWIMmpYXHoL3GG7912h9XrvBes6pB9/m5yC6mbAkzo\nCWXR7PHh0d0Yk/qKl4E5L2qYZN+r0KOHG+zG8QLDnEnTwpYMtwEPKSRXgYIq\ngtf9PZXUt+3VjjftQ2Z4+Yf7hS1eSPMwz2V6HwggQsDQBq0weakOzvVqt3lq\nW14A+p+KnShbREHcK8/BKXFQq3b9PUYN7A3oSH9ByhDCRX0Cju/qDbuJJIaO\nhDecbulyFRpnknp/3RpiGwk/xWBX70vrB3IUr7aj7gMFlVcylXuwcVU49KNf\nodehGNjrWvF9mdnfKeUAvmJGsrJ4NMjMoPAYJmrzAZLFiZOfW4Us4jMcb+lj\nPr6lqKouqxIoWtcHt4uKQXRZIKxdj0mmsVzo6OU6AUqfbjkJ9Y7OgBKLK8JH\nTMVVCbo+uQGZx6fM/KkT0RzWRnc+OzXt4jD9WRzNGr7A5WZGUT6EETzbKPwt\nI7RwPC34tpteh74aTG0+SWjpFZ93IaBB2KvyI2b7y9lKDCL8GERk5OXMWxPK\nt3im\r\n=EJSl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGG1gMvqomu2TfMlZCDjwZKBfGeBLaswSZIAtIlPw9aaAiEAnMn8E1EG0F3SOQTDnQ4weVQFSsX1IpnDKPyq7DwqFSQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmhht7ke.0_1616230362734_0.819078474961304"},"_hasShrinkwrap":false},"1.0.0-canary.kmicd1v9.0":{"name":"plumier","version":"1.0.0-canary.kmicd1v9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmicd1v9.0+c8607a0","@plumier/generic-controller":"1.0.0-canary.kmicd1v9.0+c8607a0","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c8607a01ce1e4c86259c9d35d4c5b6cb8fa94ea6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmicd1v9.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-IsfB6xuPJvNuNm0N9SvUxIVkAJ+O9iaLn20P+waDQ+/KpgSvmWFaXL359pQ3hxGXLdXsbC5w1U29hcXPXH9cwQ==","shasum":"29afa4ebaa3d9ea3df676dfbb7d4fe893ebd6a5a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmicd1v9.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVoBNCRA9TVsSAnZWagAAdLEP/0To0Kx7BsysoQz6hE6X\nRIdvemSlm8L1f+Mrb8U3ADj2Fy2AWWxRePDyEeeCzx8O/VDBDPPMnEfdLKkd\nox/w05x7IV0cw7rGD59HBHNkUqb9aH2WCSFaomMQt+w0HCh/EY8qzriOFgnH\nuIYBYDJGdosKB6Z2ADo1oNlnVx6N0mr0ZMwK8T7vT9THYqKQW8aXdqd+CtIp\nuIBKKkb/QQ/vKItKxfld3YpK5XMabsD0HmqUt+XvfTcMX250LwdK3qN14RAk\nuLtRXlm1JBnEX5wHsSwG4P7SWWmpzaacdUsy/R8Kh3sFc1oPAZc6R5pkgeBt\nB8xLrVF2u/Pycn1wnXf65FEeckm4FKwiqi7baUMB2jge/BIysa2F3ln9MWRl\ngY3kbv5ugtwfJJ2aEMaYd05ozCrmi+yF/vdJMa2JJmVGUt0gfxd/1c6/cuZ+\nErrUfq//QjSFMAP1vuiw2X0xeogAa17yKFHX5NEEf2wlCED2XX05usd5yfR6\nHqQbJ5x21Ft0FJjUG/M66AnZYi2cDRgXbe+7f796rEqIRe6loZWcIQcJNeLs\nzdcvdB4j5rpLPr9s7asiPyBKvJMIrJWytBDMeIhA31MAFJu+kjKOXmAR4X44\nlXrTHdL38PEwORQf30ogJ9rk5KUlomgSVY7ESQwoQ7I1VHOdmpWYrwnGaVFu\nONbj\r\n=EOVc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPqWRao9Qh7fphvO9CAvvizxlqgQeQT/aG+go5WyDYogIgFNqs0VMB2eU7axzF/vXjHj//b7cAzZ6fACrqXTvdftU="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmicd1v9.0_1616281677095_0.8373754055143154"},"_hasShrinkwrap":false},"1.0.0-canary.kmie38bd.0":{"name":"plumier","version":"1.0.0-canary.kmie38bd.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmie38bd.0+98efb8c","@plumier/generic-controller":"1.0.0-canary.kmie38bd.0+98efb8c","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"98efb8ca464411c0ea53fcf2946d631b298e815b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmie38bd.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-Q+Y1IfDCfGWp5Dotx/lgplGa9B5x+C+Pjt8NiR0aDJqQD6jjGwPnO2biAmXEbI7d+wMKFyIp+eigks7Yz96uKw==","shasum":"b4390ff684c0f1c5afe84eec3fdeb8b51cefb07b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmie38bd.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVoukCRA9TVsSAnZWagAA+zwP/2GkKzyevLuzgp4GLuw1\nSi5HKcMKxdtW9+wzhwly0kAlKmC7BXDT0rg2UtX3ugkFZm/k1uR1e1Y5aHj9\nALbe0XnM3ENV3Zbgss49g6BGZfQhff3n8PBu7h0liKSS/epIAl3LbnR7AEQn\nIE0nwaLIWoksICFsdA25Yigk7OpDtkeGLVx+5SZe2x+Ydry66VLhzj75qqb7\nSgU+6xE3B3kNEPey1HrQTP3QCOJn+3rQMQgSN5t6cwiIyjRP3yLkd99B4iJQ\nWH36eo/rqV84+BuebGt9OGUgRNxpk/gw8LzUv2ovQzHgHvosyKEgiDSi5qZX\nhAeMgXG54JWpRZQNEvRhv0717X//UK40FvKkrnD4/19HH7865TD0xXjISI52\nrOlToMSXihFGfFfBIxQnYf6/+DJkKY1HUPLyf517tA8HQo4zRWiQHPb0o0gQ\ni5+ms1bP8r/icEhJj0NkJE5Asbt8YJiplPRkfoCerZO7U/SYuxPxgrItBqVd\n58SlxubQqUhrbz9M7fBuduHMx7jfjE1c8H45oEaGe+Xu+VQLuqLlbez+WKkg\nNEC/GwwTJGif4PXe+QnpwLamKmJEjc1UqbrvES3QjYMdqmkuejMlhKHNdYsQ\nTq1ZbWLSBpgy3eJN1koXU+gfAfJCZbFjkpGnoQ9Y7wUpflI+N+YrDwUJwF5Z\n682r\r\n=X5ki\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICxJfB2jdKmdU14wStPW6JDNvStKYBvLDono5VT8SVMDAiAIY1opc8O8hZpVzFKayz9EMmk0AFUgCpR7Q2NFiJmuyg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmie38bd.0_1616284579780_0.6912005705748279"},"_hasShrinkwrap":false},"1.0.0-canary.kmik3vju.0":{"name":"plumier","version":"1.0.0-canary.kmik3vju.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmik3vju.0+8defadf","@plumier/generic-controller":"1.0.0-canary.kmik3vju.0+8defadf","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8defadfea222402102dd29ef0e5402a597161048","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmik3vju.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-WVYowcKm7dQeZXIBg6kWeNh9Nn1ky3B/HZYVrrdBSgE+8t962da9ZIEh/LyZ8k/ObdbTgu8i9NFUYl2HQA03hg==","shasum":"bd2f7d7fda2a2a76f3cf081beede078dc30426f7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmik3vju.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVrMgCRA9TVsSAnZWagAAuHgP/j/KI909/eSMLrU2B1BF\n2kejzE/Qga2nCXeBGSsCwdv0p840eCApgJuktNDhqcmB5WgzoMilpCn30cwG\nE5Ikg5B7EVTepXoeNgvo1P+wwjqEAfNxo32E5edRV4Xt+5hN4NxyCQ3kPxnW\n/sP/frE0JvFqiOXl53ytSb/qXrsBAF2jN7IQIPWCz2WhkT+V/oiU9KolEafH\n+7xQNP4bRwxjec6Cv/XWU4fAAopJiwBrHGt8Q5I/Zry0r/yaOZ9Vr2wVdN0F\nBuRo9U4bKHm5dMHXQG1jZ2ww7i+yqjRi76+nyyDHBjv6dHaMmtJVRn3sGosn\nVk0zk4ktUjdpoZGLuYIbQjIVHWxcKW0EF530Ma7AvC4ROGTuEbTtYKzcOo3B\nMtJwpZFMnCgnY6nGkJRislO+KAvnZXNKXfcZT4CE1XxVvom0chuaWbSF6jHp\nr3DjP1RTGjVFe2VHK/+upA3jNp6OrSFaVfyBLjKO0zPmR/Y2/tD1KEqHH3rY\nYwYE3MQvQiHrh4Jd5bEKmi44fXPo0rLWb0t4JS6ZpHGUy+KDNYCuSU8aSR7S\nTj/G9pBoO77HiULDoHAKkX9fofSpo+IJJl0QE3fyVjL+40/KTpWZJMIqK4HK\nVeOVgNUhWCP1rpb4ZNpKUm2RDMtZF7SP1dZEXoiRLbX69eWhRv6xfr/iBPQO\nF9XN\r\n=kMVs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGzzGfVf1DDnmI+h3toPIC2KCjYN25J7Bcf9mRY9CwC9AiEAn2uBGLnEWApz+9feC/9mQQQ8J4ZI2NP+8Z8EXS1HgLI="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmik3vju.0_1616294687739_0.8785849712928284"},"_hasShrinkwrap":false},"1.0.0-canary.kmjs5vc1.0":{"name":"plumier","version":"1.0.0-canary.kmjs5vc1.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmjs5vc1.0+8cb5a1f","@plumier/generic-controller":"1.0.0-canary.kmjs5vc1.0+8cb5a1f","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8cb5a1f8bce088e545746da41b67bdaa3d8a665a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmjs5vc1.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-Tp/wCmnCnm3Ygsl17d5v0khDXGk92fJNo3SAtXUZUEJh27u12td+b6NwY80QiPLEs/eBUuEgn20GSEPpBku7jA==","shasum":"c2666bb0c48c05692676695292e6d91333542f73","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmjs5vc1.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgV9QrCRA9TVsSAnZWagAAeQIP/19Z4kB9tqAYWqYj43yW\n+dtAWqW1zsvbcbRA8+JzcrCQP0h2owgDX5SZncRoQa8eT46NsMZxlgQZZWqJ\naa1f915Vj6/HwipM3h7EaX8GEYpzsnIfKLwXiKe5rZzZmx9P8/vNRd7eM/Xn\nNBKo8U5ih66SM2XVfCC6EE+vtQoDg+LoXEcZIkreT7sgOEIEj06WgMRpcFiS\nRMeoIDMS9OixQ9SHwg9rYlp9Lnw233I8ihZxPokm0UhLeGWGFL0QjvZj//ky\nP/naeBBvtTD9rVDzym6cLrMx3A4liVp0EqM9Cr9dVNqaUL3MCKH6ageq4g31\ndiOzP2hH3EyvBwcCRlCEvl+7PEjCai0rKwUNJa1SCx85jZFRcONXDk4Q282z\nyMSuHubIGinhvHpVLcKCo77LSuTQX65gSxGvMqxQYsIjh5xaDOFUo81/HXRx\ntNj1dyTWgQhkjuhiIRRXN8ZKd3KL3Iob1EV4SnmrqGEc8cKHWw/d2JHUJrHo\ncEw+9EtD3f5CZlrk6UkzS0WI862ivJXZZu6UjH+J4Q7QN70vXoj0yHsvDTex\nfUHtvJFMdcv9oXIzWMuqeTqYgin3Ru/jSkrwt2ndlgj2yUkwx9jxhd8O5qI7\n8ZJtDtoskv23Lqo3sjpmY8IkzmWrLOtQnMgnEB8nlm6DEHobxboK/1rgGZ0W\nCdH2\r\n=hA8u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF8PLzWzkNUvXfsTryyjaKC59jbB2fp3b79rGvfwQKTCAiAxNDaQK/j+jhKCIQKl5QwRFR2feU+K+1uvY5GFhkqWJg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmjs5vc1.0_1616368683139_0.5522737568702438"},"_hasShrinkwrap":false},"1.0.0-canary.kmjsmuc2.0":{"name":"plumier","version":"1.0.0-canary.kmjsmuc2.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmjsmuc2.0+99bf1d0","@plumier/generic-controller":"1.0.0-canary.kmjsmuc2.0+99bf1d0","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"99bf1d0457848629999a4a383e29697055a7da10","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmjsmuc2.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-KObCC2POwp639EPtQ54xQIcCpQXC6Ge9SysMsj+w06PugY07juoy6pwsqt7cNkhyo0+Sivdf5EEPOsrbWdfofQ==","shasum":"1e10bfb98bb99264f80a5273ea4d366024174c6a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmjsmuc2.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgV9dGCRA9TVsSAnZWagAAjmEP/0JHTEe3H8hQH66/JHEN\nCcEakZqGZBzMObpK9Cs6mJ1F7iNGihG32JEy5NgE5eK6FvqsW0yaM4k7TtLs\nbzQNKemMBntbgQUgq1BBYGueRI+f1DIZARUV4wXiqNJSK4FqHPUzsgJN+cEp\nAtD86qvscYKjHlE97Qwx67zjkpRht3ZTKanWeEB9j3K9iPH7XfsERBlrvIXj\nHhDTwxpKgp9rKBa14a8CNiSbXbaddtNmly9VixZVs7si9PO89xBwEy4a0GB5\n/MaU8nRE8FgxjghYysfQ+yV/7tQxAevWC2t8x34O4HBaqmpz/qx9g6vY2SDB\nyf9Okvi+1SzdYlQ3kOqp9Y+t9+SAoBcaEA6hcyBgaMfnBicB11TFgyN/QThQ\nS89bv1Wu2PAwo4Ap5en2LD6UP/dZZKA7cfp6kWeAlWy43QMkETpQX/BRsg77\n68JIzd9/bj5a57u/UxjLFMTcz7aEPkrrSNJI4bt6M5VTN16jSVM13ZfQuYCm\nod0K2HKRFpPPnnT+7rE0xrKBu2lgov9UEEiA6QlMDDFFyLQ2kdttvZyHhBoK\nYWryTaQbaaCcIjDCVs7oYLVVbongBR6SNYJOl5smkA0GneEVzzgdUNYJwqBG\nKVwg5N/fnJxjDtNF8G/ZdaBnyXPBtJCz6M5HBykH/7yTdYARtQcGJpj1Zbu5\nY6uU\r\n=b0y0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGDXNCBjn6N7s949OtB348elPNi7AEi8ULmA/b8sDaI7AiEArMmSCJ6xxUc3l2QfyJjaFgm9/T0HnyJW4wMWLq/qDZQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmjsmuc2.0_1616369477595_0.5669573906178029"},"_hasShrinkwrap":false},"1.0.0-canary.kmjvtv0f.0":{"name":"plumier","version":"1.0.0-canary.kmjvtv0f.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmjvtv0f.0+c32b16b","@plumier/generic-controller":"1.0.0-canary.kmjvtv0f.0+c32b16b","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c32b16b06f4cbcbaff382461674ed7748101fbd4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmjvtv0f.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-ENV+/fDZneG+VCOKZZCPomFgi07dg3M0vz3F1haGCCgyY5UNWg6UB4Fg5mUfK6nCzcvp9KtJkmggQ1Nujkxvyw==","shasum":"8f8ae0ae0edeeb30a8955fe668c23900477d69a6","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmjvtv0f.0.tgz","fileCount":13,"unpackedSize":27578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgV+xBCRA9TVsSAnZWagAA/NkP/iVHMAIxA9oqtLnUrEKY\nZ7CGUZfbAhVB3F8nFCXAEKrkBTS5B3ky6YZ8Ru66TjWurZa51BO3XLrU8dP8\n2U431xJX9tjJ5/RoepbokGP1fYkTWM/H46UcNr1uFtPUPwwHS3PLKn6/CcGT\nFTGvUqR3o4sK/FqlBepCDjKzu0W2HDl9+Xi1liNyNmSOwvv5Lr9WP368Y5PE\nPhZ9M5sGQmrpuU5gormhoJjQ9Eo/QZlJD/voPof69ENkUzvIOZ1zDu/bSJiF\ncis2YwezTaEj1IntOLOUc1H+MNCjtRO/I0k6JvGPL1d/i9TlR7cjHnXqfxc0\nDp/wajAOFTBJFa6o1E6jAYorIk3qs58KX9BJJ4r3Z0RRI8CzkfzAWrkQHZ6t\ndJ21oRE63whlYhNggyjUOP++5UHZqXmOoOKKb4bIdfA8HSU2saIaHAvV+nMj\nX6qcxpauVlhWGnOGJh0liVj3Z6nsdu8QL8N9+6Jvjnxfe9A52N9ih2eSG1rq\n0S5koeoxoQYVtFZJ7mug5Ljx6Ux3bk4OltCvd+0LJjiMT10GEIOnhCj5+qHq\n6otEMzV2wJ9nqz8DBmBOrqxB8UNkOujSPkBWp0yWqHX38IbpBp4GJv+R7lsw\nU7SliDjwCG08Y7SO1FnEqtKW8SmCFdQuuaeuXT15gpqiWg14HAYVQc8aUZeX\n7X2R\r\n=jvB8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDAQ2OykKNlupEI3fwwzQ6ec7y5d9NCSytam1x8AJH6mwIgHcKhwMdQCZ6hlL0unGzoQJ5pMedIU2hQBKFy2urJwPI="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmjvtv0f.0_1616374848906_0.9641372033551723"},"_hasShrinkwrap":false},"1.0.0-canary.kmjxaqhv.0":{"name":"plumier","version":"1.0.0-canary.kmjxaqhv.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmjxaqhv.0+fec4444","@plumier/generic-controller":"1.0.0-canary.kmjxaqhv.0+fec4444","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fec4444277eb814e995fb30b75fa1706f20cab39","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmjxaqhv.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-Z0rTKJYlUKoWQVbz0hKibnflWZA7ZP8267aR3L56WuzBvaXyShVZInw+S3CpyP3Q7UcotBBnLDqAoDVwXyvxPA==","shasum":"f53d32ecd9be37b70d6de224e459699fc67d1d06","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmjxaqhv.0.tgz","fileCount":13,"unpackedSize":27801,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgV/XnCRA9TVsSAnZWagAASAwP/1xtcOAwR+ZY0N9LGXHV\ndRNsSv3GHHKQnTMpQIaXXS65AVbkCkqtZwDrCgPiS4bTDJ/P+NCSCLqd2X4k\ndDI+oY69b704MCpS1KAg9t2CvYNZssJvOX73GwW9O60zJt3fAAHx7Zpwp5Bn\nnr+hSFYde4nkeu4NBUaPAb3fHMd1idy5ao+/MeYrhfoZeqIIGst0tYETOvAJ\nUQwx6yR7oAnNIQN/sPC+l+VGGJnccRsopEIkXGtI7U5/OxV4ZnBz9YA1raw7\nSArjcEOWqNdw8y4V1t3zFkgZum+v2ThB8OrQXMMkKjTRHmGmcbzMdFrErR25\nOh+8u40hxQy2hXi/FvcBLewnx9uwNNRRCNkFTbjUMdGbUPbgrdztY8WxqNs/\nbkVhtHKyaiwL5ohyi/N7d2xOIW4Bejo2XC+U9fAC+sqaDTm7cafF9v7+3dLM\nFL8NzdGY0PEgBhsb7xy/oq1aR8q5d9UGiUKXWh3K9it3p7THpC4hwisS+4Zo\ns/t7dDMke09BmH1e2xONDtKVxep5uHzjopLLpKOLy55H53Q+n2NlDOPMAnVp\nRbFwSiRQUObYOiKGSmod/uwaFWoXBn90yhy0RXFtN95H8xT78s7d67ajwjhs\nmgUn86itnvTJkknfeExptOB6EiECeKkKtdtoDkT36iZcRkOHqpGBmGw4UrbO\nWbvG\r\n=M6Wd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLKF6Vz/jti3Vfgw8zIaZVwBIanFRLUyeSFRdmRlQdgAIhAJl7vS04NlJTBOahk2r+iknc8U8tNBYzjDbOJl+gUUGe"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmjxaqhv.0_1616377318655_0.759977618216463"},"_hasShrinkwrap":false},"1.0.0-canary.kmk0sp78.0":{"name":"plumier","version":"1.0.0-canary.kmk0sp78.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmk0sp78.0+52c4f88","@plumier/generic-controller":"1.0.0-canary.kmk0sp78.0+52c4f88","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"52c4f882a2a93899c904ce59205e8a9fb4f38cf1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmk0sp78.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-dyOqK2iDGzLc2ZGs2y2UQWNp+4kmp+Ccgkf2KWqQoFqi9LmxL3CE3ngQFaGH31WAGLAuUBBWNxZ/UB9EF/O4nQ==","shasum":"1f0e347d26bac7ec0db66d3268f07097a98237e8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmk0sp78.0.tgz","fileCount":13,"unpackedSize":27801,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWAzVCRA9TVsSAnZWagAA0/MP/AlpQ+U+zme2qlMD68ko\nK8Uepa+ZWsw25Yt3FmoGSMTs6WIMHIpg0aWzxc8iX2Mv//xcum2cBGoU1o/p\nSjXv+BXoSm143FJNPHoIVsFW9oCBZujHk2mZ5VzVj++xxTgsKpDtPTnePEkl\nPLSUuacs6R2m/Z5dcnCv3ixg2rFSxvO3y+wB04u3xpWfCfx0pitvno4Kivgn\nRCCslAwgNMJqdeLbo4NaBoRpMU8sIMlR9Gmv2laKgM43XFKqWEPx6CHDTFA3\np8SUO7DtXqQRsie+iavrbzOvai42tIPUrUV6MYDfTEuTgdVHx+PvhP1Lt1fK\nH/KOj3KK5euCrLCAUe22+mSactAkc8icaVigcJuNwz+PTK7UO/k1kla422V8\nzsubaiGoPbKXG7s+/EAYTF03pFzYP4Mv8rJVYwbYKYfcRQ9KoSw4LHEYSElB\nD7bPEvvgbJ6/oAHfkNUXBj4Ql3nZe1PSgegnX7ahMG7aVfbkNRVhjaXt4Zv/\nY7Ns3JAvzJQ5TYIgaI8bbN9U1ZreKygAT5O89Z4HD1ncA7ftNNL4LjJxndsu\n8qVet7WXMHE4GPmlEDYdQjzloC4CNy2DdMU6GzDPYhmIHkF1Tc1jxIuoV1VP\nz+hw/Pg4IJ497yWlb5TXE1AQvNFptwWHqewSPRFbCxzMttd92+P6fJ1v1A3a\nI7Ur\r\n=XA5+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD2ecMt6JJ9DH0opbvl2dpyjrltRCQZGErqLuMmdX2ofQIgHvzufLh2NEynLe+puFGh2468GfUqe+CKYJvsL0iudLs="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmk0sp78.0_1616383189062_0.21021815608423067"},"_hasShrinkwrap":false},"1.0.0-canary.kmk2n8qb.0":{"name":"plumier","version":"1.0.0-canary.kmk2n8qb.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmk2n8qb.0+f713694","@plumier/generic-controller":"1.0.0-canary.kmk2n8qb.0+f713694","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f7136948756388d9ffe3bfbb37698ee6a3be8709","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmk2n8qb.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-vdBW67YsDBYp5cryzFYu7j1vhqGxFMX4Sw5WNXHtEkcRM31mf+du/dMXIC/CtWNQy+CHQ9164WchGc17vOR1rw==","shasum":"5d1d45da80e5249b1a4e0ecc28297591f5c1a261","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmk2n8qb.0.tgz","fileCount":13,"unpackedSize":27801,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWBj3CRA9TVsSAnZWagAANmwP/jcyCU9tCxaL/rvpzM1o\nq3iKobg099CuOBsdn4OXJo8NaE+erK50tBGl4JtYmLVRtsSwicvsqOPp0MN/\nmSfv36L2R/B5TQyIERznYO7sq7Uv5D3Uvfh4Aa/+hBfsdU/CFatcAkClUfWr\nYbTV7LOeSofoQBIxeehl9zNmpcqojkPiEhvc5nccjl7/NqLHTbpHG006W5pl\nK291Cuxq5Xn22eCrRtX3IlvugVQYsr7piwFEBztCwZH2iIx3lLtbzfSWLNpT\nnY7mfbVS/Is4IVs2sGxSQ5y1ZbJ8pkVjWq1SLQAfEfxiYwv8HsBE85/AxLt0\nIO7t4gNJbAL/6z3nIBmnu7WVW557nUM3/joFOJVc0c31OzhHWdVsUk7Yc/Ad\nAxVHFtnv983OquYKX18vvRLI+7M+0JXJ91SGtl16DMkIrC3Ud6aQk2Bj4/JY\nettdGAh36vpKH0mtj5VntdMLlBy16zDddh73/BzIJvQjaz4UY7PpHFemVqpQ\nWw/NTR7i26RNp5iXJ91TfvRlX+PFmkuJLmym4Ca3+tqsRPFlzwE4ohjkLQ2w\nYfNSgumvqVYzGdnAuxcsXpw1w7yFQQ26xgDmRJjcXSc52cuyO93O6OciEHzX\nkZdSGTSOEoiAGoFwHNqn4cg2xFXhE5Z76+CyhAhNslkSNFW6sADTHzgZhh8c\n8+ts\r\n=gMYw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDz0e3RNYVnwHARPdFTtAogUPZriqwlYnI37T8/uuJPgAiAkTi/OXfpuZ/2loOnxH6mrdKCJQ+wIknDw8afNqslz2A=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmk2n8qb.0_1616386295549_0.5708814257445078"},"_hasShrinkwrap":false},"1.0.0-canary.kmk7q7m0.0":{"name":"plumier","version":"1.0.0-canary.kmk7q7m0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmk7q7m0.0+fc85e35","@plumier/generic-controller":"1.0.0-canary.kmk7q7m0.0+fc85e35","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fc85e350d4a100751203749e5e2847d12e0ff804","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmk7q7m0.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-8HRQmPg6HKqxijR1VmfvpYb7WrfCCv6wkjmknUuqML0sGBS66uGgTzoP3xYGi3kVxxLbWXN+B4ILiE1x0nYkXw==","shasum":"d4a96144835bcba767c90358a035e625563d828e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmk7q7m0.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWDpPCRA9TVsSAnZWagAAbFwQAIHuSNScXrkl5qPro0vy\nwayckRGUm93AfOb97osGzlDblSAXT0k8BYrDzbencrujjnIZbo9zUFjNGiSW\neMAFWTXbE5YxmoeLyuAqCnA/1dUgAtlIA+Jl1ZAGyuSF/FVA6+S+Cjgc3EcM\nCefVw7ZP7mxwCGr1SSk+wZQuLc/NCRdJvDma+yiHcJYI9u9sgm030qa0I+N9\nmIeXmlFCnGLXYIXg4kTBDHi3yE1Arpd3uq2L3gA7oEymW2OI8bR8m5I6sCzi\n8cf1wFVkkcxE36Hp4iHZZQk+BeVsTrdMZx+CLpvrvAdO5sgTZjJc5NXZa7NT\n0QVtW7tDmnnwhxjPE+FLcXqqQ+Q2ea0wNwVT/MvhAhMjr5AeCEKo7lDVeO5P\nqJ5k6uTlm0fd0t0by8eWouJpcVaz31Kc9SdTvpjWp5UUzSjH7CTDP1zTGDcC\ngiilFFSq+eOuo7O6Az37b+w0Ucn7IAZOKIgHfLzGLBzH3A9g/FM0ZjTZjC37\noHZl+IL0FlUxgP33MwMr7HWTYqr/vZuOkENZx/C/Ir2D1mzov788p3uzmwcV\nerax41l6OyhU7zqbwjf1KkhGOkzJqUQrcvHgQNTVGY3g+cW5cwk5WsdW0Uy/\nJtsyo1QWxrv4lcc38XnOAulTwoMVKuLrqa1GdgWklVuZsgqQViHEjz9BpUa8\nWrKG\r\n=uUHf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwDDtwbUmgO1pyVtUXlW5g41SpBnHptpbplW7dKyuHkgIgOFHf6m8ZCXDS1Unar2sfEmiAVi5l3xQi6JUqdsMuUpU="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmk7q7m0.0_1616394831135_0.19258491576987335"},"_hasShrinkwrap":false},"1.0.0-canary.kmn96zl1.0":{"name":"plumier","version":"1.0.0-canary.kmn96zl1.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmn96zl1.0+4c9d498","@plumier/generic-controller":"1.0.0-canary.kmn96zl1.0+4c9d498","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"4c9d49856fc7792cf208ae9b685b1a386ac8b942","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmn96zl1.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-aOcRuSY+6s4ve2yDTeoQMTOvlbsn7Rp7PaP8ZozkxyuedgIVh6YApzguGmj+M4ZadzEAU7z4AoJNZYQozpUBIQ==","shasum":"19c6f9e16f5f6b02deb380fb65bdcf89f28e3ce3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmn96zl1.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWwiDCRA9TVsSAnZWagAAL9AP/jvbyFCN6BqVwnJx2C20\nEnYXjkLWPw1AvGf/nie3BkEn04PvLMQ4bIpUg+eN9rQ0UzQrXMTHY/Lbp/6w\nF+hJQp1ObxUp56mHIJ6Vhn9eLocCOzvp1BuUzEZDUieJcxX0Of65kCarSe26\nE5bH4lAKd+ePnYXvIp5m+EEbeC/gQNeHYxWQgWtvWmex5GemgIsiz15/KMMc\niMbDqpVnQKDGL4eaVaPvwBOijPtv5T1hVHn5N/8seKCM3uCw1mA8KGvm9WlI\n/WwuW21P34vzoIkSMeVE2bIbtowI6TAc/I4h7N0WZlI1xmkd8HY15ABwNMZr\nPFxV596sSeHuCo3GsNyoXvqjySxrSdVxRq8J7gRolziuX8DYGW0nW0SlobZ7\nr5OGzEehk1VeWyZsbKut90n6rGKrIcogQphz4KAjEeYsKx2oANnmHSOUgV2k\njd/XK9mOQrpE3PzGApQrSMz79UIWat7w+WKwvIV1PtN5ghwZWD8mYf3VItma\npFOtU6mldJ0B55g+iEVUVeJzwaNrQal/+PcTIZvl7niAXyDkHp5sdy8KZSL1\nzgFooayrFPv9FhtxdEap8qmVMN/TJxcCFxcgkQ2tdoxGKe7S3QbYjvXx6ShS\nfRjGSfoRuiBTS7lwteyfA5pp17K7DdTWXFj52CY+Z7YHaNBNZhRFS9yo2C79\nbw5E\r\n=gPFr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDWmcY0qAvKIR2fX2oU0PKd+yipOJGt77Q08yWuP1NmvAiEAlTn6c3Am+OGNWgq8J0U5/vv0EWjhAkPTx71D1ilckW4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmn96zl1.0_1616578691491_0.37421221427059237"},"_hasShrinkwrap":false},"1.0.0-canary.kmq5uqhq.0":{"name":"plumier","version":"1.0.0-canary.kmq5uqhq.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmq5uqhq.0+c9bbfc2","@plumier/generic-controller":"1.0.0-canary.kmq5uqhq.0+c9bbfc2","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c9bbfc2ad6a465fb310455e3d64889cea5ee1f72","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmq5uqhq.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-/COw1wIFJ9gpmPTjo/agr4ZK0JhPaiSjJUyfP16h3fWaGuS1aIDO3+zi5hpBuPThOGRRVtqd3fDP18FmJrwO9g==","shasum":"378c601f7b6ce68458ee9ef8eba5b6f0e3cdc29f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmq5uqhq.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgXbctCRA9TVsSAnZWagAABvEP/jzMkSVcbB29AGKE/11q\noCu0t//boXqeHQaw3MDdem8xnk8Y8gzQuSMzJbAx3hb1R5+QGUqSosPdqd+K\nSlVtMXrBIHcmeCJddw0qtJ2nUm1LCgsjsLPdUis5mVqjtkbyflMT00wAmpmm\nhAUy4L66NbgkKfg/Sm3qyQSWx0YBG32GrMzh6EC01LK6rOAx1RzYqpXc1LGt\nOzyKbmEgmLdrkE2XNe4+ILPqFAkg4PAbMLJaWZW6w85tbqD28qlmn44mUs/p\nVTtO1r0aFTDvo+qT07+F/V1C3A7NbeaWadwtVO8zYGEI3mp8prFXpJz1dRTZ\nb7efoWNm8kS2dVUAPsDOMODyIeYdMs9FNj9wkltpVzyQotb3gF9IwcChaE7t\nZajpK4k1h2BEYhpdtaivb7jwhOm5//rHKO7IJ6g4MPk+ZZ03MOQSsgX+6t5m\nJdA94VakezJ9TaeDxtm2YBmslVQRHx/rFR/pOzBGxbd3TiB85RorbiJaoTBb\nlY+hrY3yMc1DzXsugLEgIsBvhYEAWhkn8CIWUvjUEYVRtMRKngFltwgN3fbi\nSPhM92eqk7Ysg7A4/8liF3O69jl06IZPSTH6XgU63uqxp2qQXftP+LQZt7cZ\nxeKwKCZ1nUgLUR+CxTNRSDKBjdcFf6E0MTEQ8OB8p8zL88WwijGg1lj08r8l\n9WiX\r\n=Ut24\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH3IKgSZajCXGRctmW8ytDwclEZ0GDhv3UsZKDsQKlQSAiEA6PfRzIqe5u0iespKql09EtjPaNc6VRvtqxVsq+Vfoqg="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmq5uqhq.0_1616754477267_0.3245435179013454"},"_hasShrinkwrap":false},"1.0.0-canary.kmqwm9wn.0":{"name":"plumier","version":"1.0.0-canary.kmqwm9wn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmqwm9wn.0+0e3463b","@plumier/generic-controller":"1.0.0-canary.kmqwm9wn.0+0e3463b","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0e3463bef45c0af7a83cc9f5cea86f7533c7695b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmqwm9wn.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-2kSj4fgS5h0L9kl75RCrAo4mjxj+QbmTUyJUkWawjAtcQelMt9rOn1y+g5oT3drKz8YrrAbCuL5+kyYAnObimQ==","shasum":"a267f94660cbcec0390e3c34294cb25bef6a66e0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmqwm9wn.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgXmbFCRA9TVsSAnZWagAAxyAP+gKOd7ZuzLl6t19mvbum\n3wcDeVptrGT+frlf3Is0juTwceL2jR/F+iKjaYwBdHwJ5/vHmmSd6O0XmHO1\nMx0i8LoTIarDeqhMxxAYCUaDJbCZxBF7nYWdY4Fv3cAVLtGm0xI2carmDGLC\n2penqSIzarLmUV+Er26rLKH3UVzi5pEFBf7RooeaHwbydSoHpDTG+4QfJ/4K\nl0efdRJlwwKmu5NVLoOicnz5bgOeylq/NNWOU1ULBawls72XRxM45KMdd16Y\n1RtGBuH8QApHiOxzMVT/o3zJDGpEpImTr6znZ22ZCxKKrjvWcLeaZLwvtYul\nU9gyLFh2Fgr2qD6oarNvjbIe9wPTycHmuenA1A60GKmgjkzjCEjFa0yCAcFL\nOkrOdu64mQCnVZhXbUHC8TPIw/GMyUvivZ4DD8xFwzDpnKVQpyEiBBlv60cD\nTWaZSO0bNMTMSe0NBgXeKgoN6LFwIqHLFGcq1nVNUqNXNlQ2B0wUM2r2l994\njXwq/AP+NpJP66EonRgeidZ88GWexxSUYT5+LDT7IwVv+ecL8SXPMrlYGnoe\n6sz4a6Mda5DQpfgJ8sPE2JEjnvugbWlehoYAyYQ8SxGWwMaVpi0Oq9aZ3K1N\nLkH+fBT5TN9q3iw3KFP7RJWze0Kq5/8+TwG17A+92u8RQUGVQWStagE2QhrN\n07Jc\r\n=iD3e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA9eWgguLN4LfRMDCXuus9mNpRyRyGrpp2T89fYSlzGRAiEAyiFw96JbjVnECp6GbYr+82ljx+nDWb3DGYOikM6k5i4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmqwm9wn.0_1616799429004_0.2715665857204441"},"_hasShrinkwrap":false},"1.0.0-canary.kmr00yo9.0":{"name":"plumier","version":"1.0.0-canary.kmr00yo9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmr00yo9.0+2c0ef31","@plumier/generic-controller":"1.0.0-canary.kmr00yo9.0+2c0ef31","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2c0ef31c5c7ca35b831ba91bc51eeed01f5cb896","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmr00yo9.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-HP7pz1RM0S/KbZSJ7MThhLRFZe5v5DLNcUqwg3Eide9uP+LzLv4VotW8HaIatcaNDQ6fnCAT7bAKWZUCHvCRQQ==","shasum":"6eda34e07f6f816004a31c6f793af7ebd4cdbafd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmr00yo9.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgXn0hCRA9TVsSAnZWagAAd4YP/A6kmPQAUSfIJZOPox1z\nWji8lKqDANeZo0JpWYBi4cdeAZtss/7HKz5+51SQhSJJdpsbP4aFPM6dM8KN\nvkcJ0PzzT1TsG3j5kmH9WKAis+fv8l5PD9+o9UJm6wfxyWykkkIuzBoJEEmg\nxm4GkeT4HvzdDHKpG7pZXqaykE3dbyCcmK4H2HNXOuSrumfr2sf2B0kJ8DzZ\nATQOWsm8NxxHzN+7okb/7jUpDPBU04EVBnMrL+CekjM5PyVcAwCBjP+Qfb7T\nrOKFLKrm+Y2Y4s3S2K4IPN8AqRiQwmb2Dsc82wFaGQSzQOQL+5YlCoCOj5KA\nDdSe76GxPZUa+azBe6iUDp7151k3UJmXFPXQNrwAXQ9opD1rXRewRqEfNz59\npFrLY2O1aUjqevCUFwGVswlxcC5pCAhvoxEQJCbW6dR39aKY3D7hrkHSsk0b\ncbzR/7ILotg5TBNyCBaorfrVnMC/9u4yo14MlUZUqT+zjuzXL5+mNYOTd+Te\nvBwfGZl21RVexTVYM67g7t/XhQE2DZGcjZGJaDIqQRs3gQziPoZx23XKVXA8\nXvn6xPWJaAnxQ6UTJtKxzLWKdcfsfcYtqKq/EGUfncSqlEfX6nWGWX8bflrr\nf/3LZ4E9B0rylbtwr4E3OEurUP2zwB2vrPYZvpkuXLYFDrZOUgVwVbTAjfBA\n15Cz\r\n=SRPC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDqfzXOXOqmagZGXNax152fCaeSIQUpDFD0iJBY16An7AIhAIrCBtJqZnc2CBc4Na0zzonvvCcEis2BahZS91oBTh/S"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmr00yo9.0_1616805152670_0.3711288614348265"},"_hasShrinkwrap":false},"1.0.0-canary.kmsfdxhp.0":{"name":"plumier","version":"1.0.0-canary.kmsfdxhp.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmsfdxhp.0+6f60c88","@plumier/generic-controller":"1.0.0-canary.kmsfdxhp.0+6f60c88","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6f60c883723fe651d3ddfab23c65246e0184b0f8","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmsfdxhp.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-0WrK505wzvuce4e9IFoGicdG7v78dl72HdLJeadAN2sY36M+ogDGhzmIjzcNtGCdrei/S35fVN0Cg5/vju24Wg==","shasum":"97020c00b28b60eababa339f63a932abaad8323d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmsfdxhp.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgX84bCRA9TVsSAnZWagAA+GMQAKBQnL5lhaeH8xRgoqm8\nppuuZaaAT8qGJyMR6gam1LWYkARvBP+MgOTLgxoY6cnJoQAnbKJe8juiWLVN\nCPOGFSX6log3hdgL40yb2iocV4cuGpIMWmsyQAz0+nGNCeP+dOlA6zrORWEi\nLjLtWCWDgJ9miE38dbqFt5OZkvpmW4TOxHI2S0VC8n/ZcmmAeJeoOA8LajtZ\nwd+Z1HQCY7iy08y4JhUivNzHP4Q3+432296GingqRHxAsquoWari9BSSKGcE\nDdLdtzBvskShz8ikAg/3Kt1Gy/lZdupm1lu7IaDK+XfzwfwowUgDCevGfvzO\nUuJrRB2AnV3tkHGGAFlu+dvkzeLHWfkr2upQeZyzOJ4aKWdBwd+LakjHoN3Y\nluSscikTQOJVEnD1/opd5uWIGlLPGQaMpQlaDEyFRW1ntGdgNlLfL6IcZpOf\neh1/iQfhQrtXZCUiIcb8tIHKo0pAu3COcocHLa7Fpj7T4/K17lreiWAEkdwD\ndm5do+M8OlffWKZmiqI9oeSztOrNB2G46GFtGVtLsxGv2BpjPsLqjj28y3Ds\nFNFOPuHyX/x1p5pya1NwU+sd4tOZ196Ixcq1Jo/1cMjBQ7XqKXEDNoLZtroq\n+rKyxd8k30B1fWc33AeHDD0ulvUrda+QLxsQ33GqQVIlMPI6RyxsNQi4qpTs\n7G3e\r\n=saOM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG3SmLdE+jv5A5IednQ6VxHlx/gAqnd9bEQH4oW7hApTAiEA2HpxhncF6tA4kgTzg1aputX8j+zbs32B8OZx/HePR6c="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmsfdxhp.0_1616891419460_0.18704457111441797"},"_hasShrinkwrap":false},"1.0.0-canary.kmwl4pkf.0":{"name":"plumier","version":"1.0.0-canary.kmwl4pkf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmwl4pkf.0+029db4b","@plumier/generic-controller":"1.0.0-canary.kmwl4pkf.0+029db4b","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"029db4be21a9ea18d222a7a8e8b371bac74f34e3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmwl4pkf.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-vzb62UT55Ty+FZvHeHyVYfAXoBiQOsaRdbZfE4JOStlrSESodMGfhXPdsbyHnMbyKj7Ha/iJpVWOLS/VchzKDQ==","shasum":"ab638b329b54b3a0e08347fc7525eb383dcd1119","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmwl4pkf.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgY6STCRA9TVsSAnZWagAAMsAQAI7E6Wi52QLi7jyuPHMm\nuF3zaP0uBlxebjvcQ/EHHA7VmkxfhqRTL3ai+ffpVdVARgjZWqGC3XjQwFT3\nMp3RB0XaMTos+WTv9OhopR2J7Hq0iu0Jb3MwGYK0jZcGK1+l//NhFdpsZfju\noZYlFNxy9oRDUri0KBtTvWSZ5osjygPcY0Zgf3wb+/VcLfGRLZw869jjEz+5\nuGZDLXkcAsAYX+UsJWLEP7O42DzVyOzPgZMKWgH0FDLTe5RLwq9/VAE8uuIu\nJx57R09MWtL5XKpS7wkI1CiisrVah0ss3WweaWaH/IeXlyDt/tJ5aCa/xrgg\n3KIRMErHif49rR+SkeZIUbw9gAcYVwzeF4SSR23aW9QuNnmV/ucDKX5DSFl4\n/VxHzSmogQ4KX7n+M2xpzRPnk26ObhECqjRZ4XjlvX4z0Cma97d0kqVOqRTx\naQ824ch6jxCX9eTw8Qm2TiY3+k2ljvltLEXiT0JYjWOFm5TFmqddW9F2qrQG\n/E1IGhkRBdip7BBKXpiVWyYiHh31BQIN0jEr1UGW2mpulLvZjzF0TSyw3Q2z\nq6Y2JvepOdVrccfbluy7dqYpC5ct9GjPuDCis9wI70LgfRylBqZjBP2+CxWN\nPIXYbP6PLNgmK8Pg9oxNCG5k35rxj4BR0HgTPwF7Na4kL9/YLS8nE0/fvRUT\nFxtE\r\n=mn09\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDs1P7kvidgN4dIWmGEVZxkk83Y+r1TTJGLrwqc/+Al4AiApvxxFmOiYOok7xhu5ckuQ8/rXCrNU+7rPq2wEzCc7iw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmwl4pkf.0_1617142930754_0.6392393612959266"},"_hasShrinkwrap":false},"1.0.0-canary.kmwqkr4d.0":{"name":"plumier","version":"1.0.0-canary.kmwqkr4d.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmwqkr4d.0+dce4a10","@plumier/generic-controller":"1.0.0-canary.kmwqkr4d.0+dce4a10","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"dce4a10348c881957790fd528578dcde42a4a891","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmwqkr4d.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-1L+Exhvu6JDGtiebn0vn3QVozSpuUBkKS9ESsSqotFRJ/aPCmoWF/MbRZgo2GDZ9caCOXgK6WWQkU63lgAw2lA==","shasum":"a5f7f5556c666354a7ca384dad5e76c8a5702800","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmwqkr4d.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgY8hNCRA9TVsSAnZWagAAy+0P/jdG+ZrA4PLPi7AoOj4b\nyw+gqf+P+Tl7ulv8G3MYdCGUWHRXOtpbeApw+SImDtuL8epPLtD/IujOW83L\nTlm/8nMGUj4KG6aC4iRg9MAMFSUNvKrot3516e0JbsJAJtXnk2BrA5yIp3kA\n30deZ/vRDLl2eG9WzpPH2p4JhSGYaFYpgZcGZNmVjcADvbHeC8gjmFd4T8SS\nUA63CEtgQoH/eI+efbNXkltC00W1ZYGeZvK1mFo2QdrLj7zMsxSkyjQztDt8\nBAeMavGpfhDA5PEXX9HC4VUmnhz+uCj1X5jorIUh+xJoQcZX66MhVEEaFDII\nWrT1niyFO8WMmCyyuDZAYnGudXXL5c/5m3sKVzbg1NaXIKLzbYjYAYqygo9S\n9KEF1N1IO8Xr43IjSkDbZU+9oxOSdr6GOLDdRz5XxqxKiAdGTudsw0T0VK4p\nhkz+QZvV5/fNVnNsnQXVCyhJKzOnarPp6DBZM0GUHbseKRoNEu24f9q7rvjj\nwI+9NCZJvZzlnyfQ1b/nxrkp72NoJTQPqIRikpxo2cBuE47op9Gyoqv04s7/\n0T1gm86nGASBTPD+SjLi/5jewSum1FV4oV4sFyNRvCPkF6xgdMORHcY2bydK\n5PehR539wWLZnWRzAX8A0YzV5gen+D//biw1dDEwDaFMc31hwjp2NZdGQBzO\nChaX\r\n=2I5v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDv9qWhXIP6tmJWUX1ak6T4sGAxZEt5U106IbGDfd7f8QIgWZBM4/adCaI9XuZUPa9dQcdZzl93B+O28zzykiSI5q0="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmwqkr4d.0_1617152076793_0.5662184860918558"},"_hasShrinkwrap":false},"1.0.0-canary.kmwrmm9a.0":{"name":"plumier","version":"1.0.0-canary.kmwrmm9a.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kmwrmm9a.0+29f8546","@plumier/generic-controller":"1.0.0-canary.kmwrmm9a.0+29f8546","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"29f8546c70ba7ccf9c9cea929095a11b600dbaab","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kmwrmm9a.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-pRcY+I2aSqsM4KIZ5cUmWtUs69zE38ocLpR/o4K9M0alpTBDuMstc7otEgchje0ZmXHS7/0gUmA9zZnIrQ/2uw==","shasum":"7eb1f92b895eeb9048ca8db1ed4112c91abbb3dc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kmwrmm9a.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgY888CRA9TVsSAnZWagAAoYAQAJDZOMeYe0SAsT/enRqV\ngnXGDbsaal6csIE6ypXoRzpMLe3QIuP28okeO4S056bwRFtgyrMnHQzkE2Xw\nXfCBRdk28sV/cVSyHuhU1RHZ678zgEwYFpVWu7FoKCc6KOgctluIliqskmok\nkxaVJLdUNavZxIJu9wbvLgH2oSVNklLUStqk3lSUG5swGZkhHPoMGiGuUT/k\n5wT5mAHsXRoWybjieqBweFS/0NECQjs/scqX71VsoetDp7ufs98/mdmmnJt1\nYZqLoO5x6M5bHVXwl+E9/rBsSCYR2iqJCHqfLsHeZ3mfLGhimS/T8G1o1YQb\nh1NPrXvRfaJ8Sj+8HBrPvdcBioljB1+iw/vnPRvmK8eXqUe+2HPRKn0+ISQg\nGyXwgqufTlYLvAihTlpFNcak8pWtG7M8UITv0iMnYfpUMAb+ZVGDXx8WphEQ\nA4z0U9OApMo6B53zyczIq7hfiCFSMX30Hv8g0w8b+F6AIgNMNxGSFqJnmV+S\nN3nYYcx38cSxsIzYA/1Gru51evojUgxsIPfUJNNXLjSZpxYMDB9MmH/KFWU6\nTmWHDKD66fRmVJw5rd74DQHjEH0NJpb67Vzg0v1Hrpq+mWEmO5bSZb8twV0j\ntVwuKZzzvbN4jkcBDx2HmOGXa1p2aOx1ZhbrPeWhq5gIwT4HAae1yzsDj7+h\nsfbK\r\n=YUo+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTRJh0HipDYdEb8SCvndwqyZ8WOixA7XJoKMUJpChmPwIgDNvA3IQr18WEXrig7fpR+yUjEoJ4yvafYW7TWBpPmzw="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kmwrmm9a.0_1617153852427_0.5118467805589695"},"_hasShrinkwrap":false},"1.0.0-canary.kn0utu8p.0":{"name":"plumier","version":"1.0.0-canary.kn0utu8p.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn0utu8p.0+bb3cff3","@plumier/generic-controller":"1.0.0-canary.kn0utu8p.0+bb3cff3","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bb3cff3ab96444fdc55586271ac8d0524f0fcca1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn0utu8p.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-E4ivcygs+0kZb5AE+/uZNMulxQiDKzwkmIaLoUBO8L/Ss2vxb5LAwlmM8w0sxVLfCwtXo+XDfaUw/ZKVGhldoA==","shasum":"405b175b46f865f145f50c6840f764265a464b1e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn0utu8p.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZ5UACRA9TVsSAnZWagAAp+AP/iCwwMBQuBOdFjr0ke4C\ntVX5xWYjmGICTBVo0qojsK9KyIchdAQJYU9tejLPuvcRyyjEIsSrb4dAHvWd\nXktOObkpoQFMAkR53Lzmtq9rT8H5/ymNmaZqM1CFUo+6BBP7987ru6MGgy4G\nAJas2+BSczU+1YEe/pcK6hkcdWCXhTOQF7m0SpxNOqByt7dmhSY0lFEHnKwF\nnarSDx/mC4TuVmqIUc/AcXwRQQSYtIZWo3kS4tNOD+M6TLL7nPA2gS8otERZ\nzA4CGFhXkIFqsDt4VeM7ur0r7npMf4d1dhmp1iCdO3wk35sYbVI5Eld/pQzk\n0C8HVe6hslquVtCS8dFynv76m4lGFjhw6oiMLa61Y4l14O1LiwMbdsAIr2Wu\n1aFNRetLIGGycd2PHH7Gm8XkXmhbBIwFgSed26aJJvFAxFgjaUru1ZMa5PLX\nPloF7lKW9jMvj40s4CI/NtzJPjifUcyzf23SHJ3+Vsk99Idljp6iFphWqj2/\nG8+udXyc4oqFX/94uSkcbx02xDFgEDwJrWQryV3NtBN3wJCn6stsffVOR+wH\nJSxy9SdiL0cnaFW30F9/NbT/p7ds7klks7uhzcxGOqdtB/xip38XIHSPInYz\nTHemQknVuzZ9krWum8OpSnHRq4D5qEcaByiagdYNeOtfqhhPJ4FatMHqMYAS\nO3Os\r\n=JmMC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfKO1BUL5DWStSTYvFsJmq2yLtUVPZS1+XDvvwHVMfkQIgd/n0Cg8fCpufyouf46WvtnTBTUptAj1tvLUq4fSJzUI="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn0utu8p.0_1617401088346_0.2578988326691669"},"_hasShrinkwrap":false},"1.0.0-canary.kn0vwk7u.0":{"name":"plumier","version":"1.0.0-canary.kn0vwk7u.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn0vwk7u.0+aca2ae8","@plumier/generic-controller":"1.0.0-canary.kn0vwk7u.0+aca2ae8","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"aca2ae86014cb19d65c32abd7e67e20a4f52b0e3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn0vwk7u.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-c/YTPnEb/S5Q+x+TDeVsIO8gl489DXvZqQmpqrhP7muKF/BA3B/4i/UjZg1AowvxBxEfkTCd29xhHzYyNz/EKA==","shasum":"d48bf50f04fa5dfa1564d08d361df0eada6decb7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn0vwk7u.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZ5wJCRA9TVsSAnZWagAArQEP/1ycTmIb520wOSKe83ut\n3pQjxG8Pa2REeBfAvHPBUnfP88OgVlMDr6UlQB0TNh+qWYxzaS0S2wuMqaFQ\n6prFB/Qn3ajVn7vEbErMaKIVcltODVOeLhrnysULKY/tNGRdAWYAK9nouSEX\ndFn7EfXk5Ejnio6oIoUoYJxh/hldBZCgjFzzbxaYexUmuosRkyqToMhjhPk6\nUW97+LcHdop8sm753J6ZirG2BV7HltCn9OfMB87DL8+9I0KyJ0WA2ag+pp2K\nmFp3kFrnOb3hkQ8x4uo2aVjPsAQr9iBnLuK7V62p45ox8KiYDR6UAGUFK537\n1gMEc7ZHDJvQkj+RstqJmOihxslZMBJRlsnOwSj9oVWR6zQEED6Ztp2A8vg3\ny+J09LLm2Qor/VP1kvES26ZDjUvQ0QxWCTy6lDP2D35KgRHLLWgGbo66qwDH\n0tIDYsS1N5xRxZjCB7Up7swl3H4aMEn5Hsyqi14qFqNr5/j3Fz6WqIE7sAj4\nINK4SGYWGt+voAkE9k53k0Bthr3znTZMdMzEyaQ7PXBxXDQMnDtXtjqzfwfm\n/p69AJ3NXcC50C+iPU+4VQdrEViGIeBE904HBCjFvFPxHWBes6cG0+JEJDUT\nm7wjOjBxReylkRetcdy/uRXz2GY9ik2r4h5YH8TSQrjiM/fmqPp/N3tXw5Aj\na9vS\r\n=qnTO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBFcOtkLbYhH8toWLRKok6BBSKfqpvmA+l8yad92qlTqAiA4rEkWZ67lnB1jKNM6XNnAg2C2yCqI0h55nji7tpZuCg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn0vwk7u.0_1617402889241_0.8156343385969707"},"_hasShrinkwrap":false},"1.0.0-canary.kn0wxny3.0":{"name":"plumier","version":"1.0.0-canary.kn0wxny3.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn0wxny3.0+ae4d798","@plumier/generic-controller":"1.0.0-canary.kn0wxny3.0+ae4d798","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.9.6"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ae4d79850027ceb6d6cebdf68cbf0b9f89a4f355","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn0wxny3.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-YrkGf23ObKZX3BHjU9GQ3GsONvojRhW2WpGTDyZ9ZIy0ryt7H91HBoQLX3EPFjrdt2YfrQPnCjl6GDNX+W6zIg==","shasum":"4b5a42545a2f29f17ad28bcafc82b921eb94defe","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn0wxny3.0.tgz","fileCount":13,"unpackedSize":27822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZ6LNCRA9TVsSAnZWagAAT3MP/2lXSkgMvlJG/3uugwop\nX1xxZ+VQ+iWfGqGnEH9tFWhZE1h90V1EqNL7mwySUToMMRRFQX0kAkXTC2sz\nRZ6qzZDsJnHldA+MPsAdAVi0BlGFAV6qE28ALFqbprxMl+hA7VWKRR1kbuA0\nPFeC5uUK42Pz6beokCv0MXo32hvWz9DNE00nhvaJwHw9m3zAYjsU0FlP24C/\n2rwh48tkvk+YsKCRZo91OT/RoittyiFf4ooNW5v6sOEtLO4prTx2pgYOe5eT\nZYZOhWDV/ky+ScUGHYFPzKMrUjJxZU3wjo0WglzAeFdut6adRi2GWJAo9UTN\nA6FtMxshabuPZrNAbEjCfm3XHWgcYPJyyN2kc+DFAPMmj0JpGc2SN4RfQeEu\nmQeBuh//oF4+skG4jHU8rEDMmmH1qMvK8oATT3lViX0OiKkHg3W3ZVAIA84v\n1BllugVnn0X10+eu0cVDiddv1t6K7bxUo0rAGMpYZQb1/jGB8E6gubKymjQA\nJrCxianwEk5JI9MgNCtStorseiKtwEjTwnlxsr/jFhalOcsBWSh9YobRnJ5a\nWoL4omN6cXk0imIHP7ZOcZ3TQds8RtmKFIBr4ZTVn487DqK/2hHRQhdgWTVj\nCh6Eg93Bk+4e72cu7I28mzZnjzqBNvFav1tNzQ27Z9ht/EcOj5Xf0qE1TWAn\ny9jr\r\n=fQfD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCrNJTnI0GaXyoLiza8FVvqpUn4LOQS69QmqfPxm2y3+wIgaN9XoTpHzdgqb/DTpYePaTOoNadZk2mMwpf+tdy1WF4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn0wxny3.0_1617404620189_0.010568962371193535"},"_hasShrinkwrap":false},"1.0.0-canary.kn0zmjcf.0":{"name":"plumier","version":"1.0.0-canary.kn0zmjcf.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn0zmjcf.0+e570649","@plumier/generic-controller":"1.0.0-canary.kn0zmjcf.0+e570649","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e570649721cbed8386bc1ab752f24f719bf3aa00","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn0zmjcf.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-3s7xPUnyxus/WHTORq57m9JTvfvHS5LHSwF+887fGYeLpmSbevlWm0xECSD5ZqQUhfZspNSEHIFLs47Bp8xY5w==","shasum":"7c13470e083a1539bccda9b39f0092b1ef066808","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn0zmjcf.0.tgz","fileCount":13,"unpackedSize":27823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZ7R2CRA9TVsSAnZWagAA43YP/iTaPjcIFC3HlNRcSl+S\nq4UkcZIGCUYe8d94h9q1ZQK/kQ56BUPgdAr0Xy9IIIOnmazJmiFls5YAiqfz\niuKscGu9Umtykh4atung0FI6WeivJOBf5SkBB7DMHzX1iLrmn7fksdMb0IO6\nJvEKKfb9pEDH1DX0uo3uGfxAkvbA/edHJvWW6Rw7iy6T3Soovq3iDkdVbEQH\nhwtP6kYW7MWG3gwI2ruiEvTUzjwY5fpbv8HApm6oUiYizG/2gFMirH6aUzD0\nn/TIw1N3884qoXpW1ckiWLwwnUdvg3UqUTgeOZ+flyCY+BNpzWSUSPJx1QQO\niPWBdu71TFCa3poUNQgPpN7QAYVND0r0vSA26qxJDyVArsOoaUlym4vbdsv5\n3wOHVV8U4q5fZgluw0GntsL5ZcBkdM9Ps4AkxB+MZ78j+X1DplBAhwT9pIG2\nwxIk3XebDnqdJhaPHlRBPmvectUb/Q2eZ/DgScAp2/xJBXpjUjJoAlB8Jprn\nHkVn32B+kdPCgkHYjMofCrx5Wcq3mnt8UUZhCwh/9vLWfi+DtENkPCwfsOFr\nMB4+HLqpwf9/oXQTbmQWWUJJWmih4CRnTWr7o1+dhy2GJvXvHbxLycdH1xSP\nWj50oAotp5fpUzMShxlDDd/Vc+nYIOwOy3yIvmnA+Niix+146zFx8Bhm0v4L\nTlo3\r\n=U0TE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAq6nh56HCvY752B2bLVkmWiRS2fCArKInAXetz9yB1NAiBGyC3xqE+0m0rAmve4n5L8hwDZS8XR54vWkYPthUu62Q=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn0zmjcf.0_1617409141498_0.1498572890434664"},"_hasShrinkwrap":false},"1.0.0-canary.kn0zpd8a.0":{"name":"plumier","version":"1.0.0-canary.kn0zpd8a.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn0zpd8a.0+5e93b1a","@plumier/generic-controller":"1.0.0-canary.kn0zpd8a.0+5e93b1a","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5e93b1a37ee5d80ddfee0972e1b4b1e26a73e38e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn0zpd8a.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-esWq10lRxP5s3a3GdBoXAyKAasNs0mu6EFR3RUwJDIL87FjCdBX6o9F/M99nkfL7XxCv6aDGVXeWAIciksNgGA==","shasum":"366b6a996536653b4dac7700ace394c76ddc4e5c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn0zpd8a.0.tgz","fileCount":13,"unpackedSize":27823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZ7T4CRA9TVsSAnZWagAA9FkP/3Byo/VNOJ4faLW/AVpV\nGexr19l/gilyY/NnGE/VqgAWjGRN1kay68zVz6NudaH/HjOal8angYnLIHFE\nz6L2nM6rQV5j+yY56CQTuH3sAzSESo+sHI7ZHkbTt5kmOWbX1pSKe9PnSwF6\nFXVWRo+ywUT4Ym8Wo3KV/iD+C3TXpjcllwT8K8GdeWIDXJEOj+XqRfvJWc/b\nPRun5WTVqvQywCIw9NTO+UJLU41aJ87fITCO3TjX/gZUfEnbjOv+PGmjaBYG\nMTWiicOkT9Hyj46BydVXhKNsdz4SG17Mjf4+YFKfrTrcvutj3JNeYC7hJZlY\ny1qKjxXNK/Nn9Z5Q2nhjFDrE80fZRXU0KLk5UQs9eVRAZxTDSTsLucGAmtWq\nDwRqw9qnQpAkbFTmykZFmGiWEsZvPi0yVBJdeUCht7gvfxBLAI/SF6D+41aI\nR/rAxFiSNnteJdk85WX3JbT6UGnbsYSET63BjC/GYlwYHyhBgmwrZ9FpNdET\njOBgUjJ3h5u41sFwZCQSJmll8BG6c/+fP/GkcOu82Wm99iGPUSAQqRuQi3Og\ner/zz9ELZ1ed6oIKJ+toXsjl/3zE7VpVRL9MV64MWF4+nWWw1d+GlPCfDsM2\n/OQblLauJvSgBgYYkSe71rilDmciTcRBiBpGt/7qTLLZGphqE7kZL/ty3x66\nWGtW\r\n=W2wL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAQ8wcNT1tu3jllVANrp2UIlJFxaYy+8bJMmgp/DdpcBAiEAvtgzH3JnThtTk6I4qNLjVxc6o4huMKme1H0lf0nPYbQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn0zpd8a.0_1617409272379_0.5278125385988093"},"_hasShrinkwrap":false},"1.0.0-rc.10":{"name":"plumier","version":"1.0.0-rc.10","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.10","@plumier/generic-controller":"1.0.0-rc.10","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"701367e40dc31bf56da4620bcacbc9d2f4357384","_id":"plumier@1.0.0-rc.10","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (darwin)","dist":{"integrity":"sha512-b6dxpy6UNx2C7j2JchmFW2SLfpo/MXg88YXjxn0/rgAmQ1y57lAha83ekDMIHuGnUEILygSlQpQ4xXQQxqglsg==","shasum":"c500d7631447d8b01490a822831749339f9231f9","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.10.tgz","fileCount":13,"unpackedSize":27763,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZ7YiCRA9TVsSAnZWagAAYw4P/1Pe6Mi5turqG0lvvmMk\nfKymI+ctF9HsHeJtRzhdo1bIPYPIfNeEe57SvX38rwzFaSSGh3D4weSTaITF\n1RmunoFq0kfxYi+Q/WsO56/b6CemyHv2dsWFKXZcNW3rnDJunoMH+me0i68g\nRyvYqFKoVXDut/w727oYqbRUgcOXy+ynEyAX4KrM/eBpt86eJGVypJ8XP7sf\nt5JmwerI6c+ldXNiUqylczpRGpRHOupWdKTdyYzePGIKPt/ZDxpdTNB0PHTm\nUZrixda13RUAvaUrq3tnD0ZA9cWLKeqbhZ1CkL8HNchTPD6a03xb9h5zlQc1\nMweVhLcUYm76RD3f7QDli0ZjuDBYvOriir7n0zUe1+CRldMdWPRrxFQJrYU9\nYq9taSsCLzLrUIwgfYowLwY7hryZRZM/fqSZeptuNwuG4bOniUVBu/wPV7s2\n3O6zkAbA8pD0y9myXyzYahk10bnez6ykIszk+0qSfro888kHFgho4dpOjQ7g\nmLjfGLOepV0R3bsnbFkHHLCXfYFqLJKYBnvFnSvVW3kI0Se9tOuZ5FL1tkbm\n/tWs7Md0IhDJJs/idVWamT5ALrcDPNkeQCLFBWm2iPp69lRvjumE/na9oFjo\n+ibCxSthSDe7aowwemIa2Rg1Jd2rdbuiGDZNHFFNIgJMejx3SDhgsnj5Ke+z\nUK5B\r\n=nvAg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDWcFyEkAtyJK2nKR5xRhAcsYvSOvH5OgVwT/i90Lvd5gIhAP0Gt04Ep4cXcwiGaSczLad2TZhSMaDBkDfrL3iZBLWI"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.10_1617409570191_0.5483724299755779"},"_hasShrinkwrap":false},"1.0.0-canary.kn1022qg.0":{"name":"plumier","version":"1.0.0-canary.kn1022qg.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn1022qg.0+701367e","@plumier/generic-controller":"1.0.0-canary.kn1022qg.0+701367e","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"701367e40dc31bf56da4620bcacbc9d2f4357384","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn1022qg.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-kTeOkRog6iUADcJ6uQN6BYXLSxt4svx5jXekdbgTDNz/beUbVmea2tcmx+e21tcK2sfn0/NLW6G83QouLZUa/g==","shasum":"06484ae2381603226cdd7b9511da23098b39e59c","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn1022qg.0.tgz","fileCount":13,"unpackedSize":27823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZ7dUCRA9TVsSAnZWagAA/vYP/jXwPTc8c4xra9yXPmc2\n+ejiFl76OlN97ud1FImnzNTSb0hB8B7zeRwDMWnOkatpERjn8SdBCvRFDzN7\n6xPWeTS/anSivF3HoDeLFIFFkGRK7ot4beJ5CkiQ8fnpFvTnU8ZzSf9WgFLj\nhAqMeKnLF7l/e2B5A9/Meu8VwyCPUyRNdabB07xGr0K3FFgGHe/5GIIZ9Krs\nUL5iU1XbC7uYpWT+6KpDJcbknbrBICoHaMB54ZJdZkizHPMWuLXMYHBNPO+l\nwme+GzEukX7kNJIP3qwu+DJL2NzGv5R+FqJX+Qw5L1N3BV0B1eM36RbWMUW4\nrfWcB1ZMweid0isTy/Ki09GRWfhEhnkjoNMDEv+BWKk5Z+BkRIJUdiETZdRA\nY3LpjWCGr82PqjFW6IA1qJCBX88vLTcM2DAtUG3R0BEhi6fR+EsHTbqvxC1T\ndfiJCBADk3d7AVMCnRYlm2NJr1uZibtjFChoLtRt1rWSN+nzs54Q/e5NsF1o\nx9yI3DbRIvVCxErVQab6I1zr6iyCYCEMXkXZOr9+TKP1ohrGd9a9cAKac7Nc\nwZwIX0F+leQMjy1GK8EQ3ht0PRMs6Bf9eqB7EIn36NYysa/2tzar8Q2MbIJq\n+Miiy33e0KhFCHmfN/2a3Nhx2jZAg0Q/y0hWm1BGK37ndpQxzI67AFSnoXh0\neOMJ\r\n=57HM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCPmeJwUEaP/6aWihMMoaVrYgucT6jYMnF1SbVVdzI5GgIhAKfwhaOqUl/YikIMEjcKzZu3LECeWYJ+4jqG55AS8Xc7"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn1022qg.0_1617409875540_0.36534054431326757"},"_hasShrinkwrap":false},"1.0.0-canary.kn2ad3ga.0":{"name":"plumier","version":"1.0.0-canary.kn2ad3ga.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn2ad3ga.0+2137845","@plumier/generic-controller":"1.0.0-canary.kn2ad3ga.0+2137845","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2137845ef39592f760af8921b62d87d25d913e32","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn2ad3ga.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-gBsFBH7QUa5rzsGh8PK2/J/QXs0+u/g0iK5ngGuAsbuL2p672DTIxKlOYWdwz/gRVt9jcZ/wwlnGvwdVceq1RA==","shasum":"3b8a59334d2e010b8d2111b71fbb72b977a59d6d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn2ad3ga.0.tgz","fileCount":13,"unpackedSize":27823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgaOcdCRA9TVsSAnZWagAAQRAQAIRazidY4DeANoo/SXz/\nrCMoFRiYD6DaQJscYshsXoxi0jTGxZXv/gKr5uysj4U826BsBy/WyRHdUMX8\nslhMMZcxgR53wcoUkhoFQ4uRzdXcQLE3PmHvwcn6f6z1i81WJTedpGaQp9ip\nAXLf2u2o+JEExE/XetQKx9TSNIhv5ew4NRoxNQQATyrU5UG2PyaSgD1uoKHr\nFUi7KkXhASiHptY0v4Ann/xfv2iGLsHkAI4glHb/c7nxFD7qNMEjGoxD6xHQ\nUA6wTt6lu27vjz+3sT7HF3rnF9+tkwWKuXVZaOVWaRNJPCdCG93ngazXm8ba\ncAfxwcvOIT+MYqbAIPaJ243M8rerKDD0d9xSrYbYYq0NQEzGQIW/0n3mLGhh\njiJdZ1siDY06Odvh6qqkJXnT2cTBAO1fsCy7bv2iZFbN0A3q4yEPh+gNbO20\nSHPihhU30UNYXV4yqfc2oStCWehMQqKFJ369NMPzpx/V/s6/KeI7FYzYU6/q\nF6/LJ55UZR4DsoA1ZNXUnvlkqgBuLUROe5qxLJQ7EpPvB1WIH8yodG4LdI59\nWDBPu3Nsl1e1O42A1UOT0BF1M4f6WTP0OEOQ01N0xE8hCUljk/OGSg7Zyn18\nn9TxDiOt82/IMeSjxPUIs2KeQXsyj9rbpg2Vjx5z05HasAMM44Acvv73MWIO\nqL6f\r\n=Qv1N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCfCrZr48p1KSaFEboEKwDDmxXY2Q0qsarI72U0kcuqWgIgW7skQPrjga/DuIRYe+fnVTtcS2Fx74g2EKzb1KZCGqc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn2ad3ga.0_1617487645067_0.9529591590022748"},"_hasShrinkwrap":false},"1.0.0-canary.kn2m7vyl.0":{"name":"plumier","version":"1.0.0-canary.kn2m7vyl.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn2m7vyl.0+cf30140","@plumier/generic-controller":"1.0.0-canary.kn2m7vyl.0+cf30140","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cf3014002a0a16351b40eafc192396d64c93cc71","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn2m7vyl.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-f/nqoQltlZo42hrwnddBn9OcMaCHsG16c2uMJURcDuCmsTq2i4yxV6r1TjQ3aaeLIyH6ylH5OCR/1cIf4pPmdg==","shasum":"f96235bab00856d53d2a645ba8150e62026d14fd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn2m7vyl.0.tgz","fileCount":13,"unpackedSize":27823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgaTTlCRA9TVsSAnZWagAA44AP/3ZWpr0UCytrxCA7JTBG\n435uteLMMWvqhVKJx12OF+pF6QpH5rFYaNzdFUuIMOl7lFcgAVrRTyFzcDqU\nDK/O6IKHHnpAAp9qWZ3Bja0oAmOl3cs+q9gC5nkiqFEp65s+Buv7PC2RVUH8\n24FVeMrTgRrQdZGPMPTNRuyOL0+D7SBGDz3bTJIOWXw8K0perbqXC/4ElO5+\nzn00WSiEDcBpc/ayUESaG6mvXP8wDkYyuuAXndUVGNZKyHfl4frFVhMum2JZ\n0LWl69sO52W2t+LjlQVfNKz7M3GmAk1+5qvELyZV+BxLqPq4600TA2NC8OnU\nyio9wdDc/C/mcUcG0c3f3AX9eJHbdbrakEIWK1+bPzNc95yBe15plYNuoXXO\nr1pCR5JfL2gCcyHB30sQH9tHJZ/2F5c77si4V71WbFNiYNT2swsnV7QeN0XS\nOW9Idz+cHQMlDPn6B8e8Prw6MR/Bt0pe7YTYiRXyuhUnOZUuXycUFo3IC92K\nQ1hlXB68mZbC0PKF3wKHXdaahpEUy7SFq5sL/6vVCrUNkHonGVPLprfMEFnW\nZD3puWfSUA09c7drO21wfbEm0IrFnmacMb7SJDskzQBF9OK24BVLFBvN6IWx\n22VylXwhypGYd6MsSyuqQSAQ35NuISDR+80vK2JKR1wbZAFR83ORdJar+roS\nzilH\r\n=cNHL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC7VTcMq5TLRwUlS+mZsDa95KpTjKwN2TEQSSgW6qpeYgIgbB3TfnO/0Ej4f+Mcj6uSWBVV0/GZZ5lLccHn0uTAUws="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn2m7vyl.0_1617507557127_0.8612238752813"},"_hasShrinkwrap":false},"1.0.0-canary.kn2n12i2.0":{"name":"plumier","version":"1.0.0-canary.kn2n12i2.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn2n12i2.0+95d909f","@plumier/generic-controller":"1.0.0-canary.kn2n12i2.0+95d909f","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"95d909f3f9f1137dfb780b5b31fea1698cc6566b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn2n12i2.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-+ePLAMRh3wrVB3b7VKXlv5kufG06tHertVP8CyEJhv3bd3I1P8ikOT69gdL9OfcbU8iBnDN1fY2fmWIIt2oilg==","shasum":"410752cfe0ffd3d8616536ac41d2f40d8e3d0911","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn2n12i2.0.tgz","fileCount":13,"unpackedSize":27823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgaTo4CRA9TVsSAnZWagAAZwUQAIg01K5YpdUd+AKi+Efq\n18ax658MB6Ife5CmFm/GNtCEuY49WkUGNQH6jtU1oQ/C8Qfsb4IpYnS9DF3r\nvLlSL+K9n+ZTpnM1HC7jUQbk0clxq/tOFLCIqrJQa/rTe1CquMytKMRZgIEe\nkz09tyK0xsvlRI8ECbUfqvRAFz6umVORnLc5o1w0dvLhNB1oGFbZGjKy/gxn\nrjcP3mlx/IklxL4VM5xuhO9bPhSGHHhPZlhO5QnCGzCTtdsAmfW927tIeeyb\nDoeVEMZ9vcuY+OgVDc+PJSNlC6mecaNYoFjbdDcE/Q8C4DymPgNYCHXVeqNT\npodhGdyFNL4oqzC1LNh2TvtJChpLSklgGgSCTeUN5+T4p+PM0mfjVpkhmEzr\nUw0kDWgA3do2POZpf+OXDdrGvJwPzgB52isL0ajTdKNyj3QOYnGEuwJEKHJV\n6BNx8HlgAATbFwbjCbDOM8sefQfrg50rcEi+ivTxV9oB4Os6F3FZwxbfhjMr\nlvC0LASFWPIsXjP+V+XD1eiSkezjnikI0Bqn8T9ZB3yoFwGXAPTBJqz1tCl6\nj9ibgjP2yFSJ+mpTCScflUzHKcLejbSE6XYmR4Vbr4r0oLRAzukUK7p3NqYu\njBTRTJ+evK721VJDdUoA7aW0IUvBzZif3lXZ4gqs/m7yBO7iL8ofcoY0yAiq\na1ZA\r\n=Qm1S\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEb2rJiCsZbgl6IJYgjKpcuqOfmaDFFIOEU02ftX6Zu9AiBxDrCv9E+kFmqp+Tj/Ie+l2q0q/KzwNxC1YHCTogCNeg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn2n12i2.0_1617508920402_0.46581333068785225"},"_hasShrinkwrap":false},"1.0.0-canary.kn3nvmmu.0":{"name":"plumier","version":"1.0.0-canary.kn3nvmmu.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn3nvmmu.0+9bf046b","@plumier/generic-controller":"1.0.0-canary.kn3nvmmu.0+9bf046b","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9bf046b10aed3ed95c34320d119e5ca11e975f03","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn3nvmmu.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-sXe+UN6G1InuZdCXw+PjM8gU6sXltA64+3qE4bvCFD67inG7mbINgkfm/YP95W37itg5nL5oxYFWZID3HTzwbg==","shasum":"326e58a115c8b3263b384b3526b533e60203d6aa","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn3nvmmu.0.tgz","fileCount":13,"unpackedSize":27823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgaiv3CRA9TVsSAnZWagAApx4P/RXmMysGJBrQcRRvpSNd\nhL8Sy61n58YmZFzV0YSmJcjK03OXzz0tVZtM2fRCYMxv7+9nJYxTHCeZUxU5\n5TTTKphXlj+vGSB6y2H3tFUdc+so1ZOyw595H2/xpO08kTcJg/pvJqvxRK43\nEFfQfHeD+jUrjWlmwMJUvgqXV2myEeqUyzTOvzo9AscRDagAY7IoXuwLEhaU\n9ERn9cHbT7O9MCRW4QtpE4BlxpwaX3PsrNGYBEiKFP7a39OCP8NGKELRVqJA\naMsa0iLk1GDpxyqW0MNEteG3A02qXDVgVcznR8I+a3Xg2Oa5XJaOIOir7K9P\nl9H9ITBObUu27ob5iFwor1oyw4S1gEKn6hS6SHbOpmWx/k+W18xoUbWzJK6i\nBNT07Rzk5es8TODxLiqoUCOLyIQZaFTBZKvqhgH00yhX8ZAX4EfY5+y/CKS6\n4NaV/2s9jKq0lsucmr9VSZTg9i08cqNZxkxGrLWrqbQcl+nR7RFMC7WJ7+cf\nd+gn1qvllYjsJcEmJFYyJ4mvQK5xTVRHdDE2QMKOM9pC45dX9MfBh0zfeNP4\nl1XFeWgwTZ3IOt4A8JwL/Zwb4pVHm55sYdh4MQzQ2V2G/+FNtHbM0z6v6o3P\nAKvJdPxKBZzDbenRAIyGlypTxv/MhdGEOJ5AxFkBthXRSzFavDBg4v9Jx66a\nQLc6\r\n=4+2b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHYMuyWNa4xpgDbJladL5WkW+PawVhJ8OCP1wr2dhm/CAiAxYVCyHbihVlXc850LHUqHT8ayrEeJQWXMVVSoAdpdBQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn3nvmmu.0_1617570807417_0.4420251232397001"},"_hasShrinkwrap":false},"1.0.0-canary.kn4iqgwx.0":{"name":"plumier","version":"1.0.0-canary.kn4iqgwx.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn4iqgwx.0+c37e9e8","@plumier/generic-controller":"1.0.0-canary.kn4iqgwx.0+c37e9e8","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c37e9e8b310b32307a5ea6305f8636f148e45a84","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn4iqgwx.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-sXa6wL77NCtGWA+j/HMLYBs9hWwtHc9eth0yQ8ZPdXRpFkojJP3d4Wnx86c03HMZ21GEslpJwl2owPz7b+m5jA==","shasum":"64e59bddbbdf8bfb75a5d4884fac9c294c4d0864","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn4iqgwx.0.tgz","fileCount":13,"unpackedSize":27823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgavZvCRA9TVsSAnZWagAA1KUP/0wz2H75esZSFiwm3Q1E\nhNDRjrKlYoZjSRPcotX6ZtWr16Rq0oUL2wnHg1kY5bHjFwRCQL13o/F8u7Z2\n4uXKzlHECdqSkHEC9SNJj4Gwrd0nsFBEeMInqwlRsEklwLyrxXA6qO/W8qV/\nQXtPRzV722dCYFv6yM9USdFIfvdn4hABk0/oIJ/vBHGByOA7b2FzNpLWCOrd\nlFPtmzCFcSmaPRfuCJZ2r2yemld4RNSXe8kOQGS/g5ukTuOAZL/+zwvQLTZd\nZ97PdjZKIKG2eJ1KldmneUnyS47jnc+/tWIGmVnsDhbpofrbDU0M9PxQaXaU\nwvoZiAmn5Rm2bDVGmStNUGMVtOeAALw5SDO44CzSZzEx1uY8aQLPr9Vulr9F\nJQWc/C2vrQJfZhrvDKtCGbGRL06yNFGLFvX7kU3B9ja1L6OZ4CX58sIFI91l\neZvRVSeYwQTmxT+KPEfAT2+uRBuEnIuES1RVuzgZcFPRE9a4Qm3au8UcDv3g\naVc4uwENqWElBtMQVs3EuMzkS+GtRQJMTbwq/+WtXcWnPYzSIUa7TdIPb4G7\nuBCASwrqdJ5y1rwraQCl+2m1BxcVbakPAiRjSLp2e7rLq6jZ22Y7U/LBasnD\njOy9z4Kexp4QQu/S8hzon6/ewq69fzI7OY7SSKw+9nuUpGg2ylkykChCqbDc\nAC8u\r\n=ylyL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC3T+B7PBbV+t6eV/cwWETGxs+X9lDpS49JGRlcUcKkSAiEA59YF5ImLxZppBJQzvvbVPN/LXQdrZi4MRFEDVCLLmoE="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn4iqgwx.0_1617622639353_0.43424969635407007"},"_hasShrinkwrap":false},"1.0.0-canary.kn6jg2m9.0":{"name":"plumier","version":"1.0.0-canary.kn6jg2m9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn6jg2m9.0+3da5517","@plumier/generic-controller":"1.0.0-canary.kn6jg2m9.0+3da5517","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3da551741d66b4214e175b92c972908ef436c206","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn6jg2m9.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-s2rz+DZ8eOeBY+1nPD2/bDoenyVjWe2VwmR2klzPrPUWlhhWbQlJcQnO+LjGkdbZz2ZfpAobvCvCZ/mLmfBkcg==","shasum":"9035473996cc20c1b6baf927e2639e43879ee893","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn6jg2m9.0.tgz","fileCount":13,"unpackedSize":27791,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbNOACRA9TVsSAnZWagAAyLoP/iqXA3AkIIBJe2RkBn/L\nKHc37UJ4KNnGDdj379I0RHzRm7erdiDIq2uzNqx4j3j44DCLV6u9Zo4Qc1bJ\nUuG+S9yEUHCBDyus41P8oxs5EvIUFdEi28bpiXVuAX6F29Fc7UIpE2HpqJt5\naV7zPMhhOQog0/y7uBBJED6YnUY/bhJlVDPzNVrkGMBB7c0TyGK8FIeyUxGM\n4zIZH6gp9Hs6Bw98c2Rc12HdEVmg7k9z36mY0B/qZjJXDzKvw3/rvvzLUYy0\nHuUFxLftMH7Nyl5SgGKz8i99YEY97p1uS11mixEC3YAKEuunC02V5p9fw43f\nPqZUpWK6eUz12BslDGdCGE2gQao1sqQQqXWNvx0a6EzT37zNCtLluOip04Ux\nrkhUtMKVyday1Fg4nHevts6C8FGSNCVS2Rpfff7XNwWbl6jq6V6ZpDPAKc4G\nupkTZKF+Cv1EtfH8ROFZEtQVHff2luUr16KQyE3nz58auKWDIXb1aHqtVicF\nzR9EA9brFehfS1eD7JLHa1RHN42dH01AaC5nIHG/3oGM2h6maWzlAnajcIRX\n7x5h89vhX8KmfSSfECQAS8rrq4lhFF8G6Bkvyz2FqZJJfegckpmAl5BwnW2b\noKX3ojTINdiQPuHib7KPnekRPzT5/j5ei+4l5KQ6DFf4vvKLbwXCFRgIMaCi\npcjQ\r\n=cn38\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAoK0dz59elm3IFqfa9NDtnDxeLdZ0rR9NOLuEM3fJSpAiBd1B0fhoytHk7yCzAh29aecf/BNQJ5FB3Dj8cwxWXVMg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn6jg2m9.0_1617744767529_0.773369883964927"},"_hasShrinkwrap":false},"1.0.0-canary.kn6nkglc.0":{"name":"plumier","version":"1.0.0-canary.kn6nkglc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn6nkglc.0+7b946a2","@plumier/generic-controller":"1.0.0-canary.kn6nkglc.0+7b946a2","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7b946a2bfa19ec8d6cd51890c4fd638f52c8d23e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn6nkglc.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-yDBswzOR2b88eV1ISiFxXpJaMZOwdsB/+xbWRmYdoSN0G4cxVmaOpUbm7ycldEwvu4FPHoCfzYVbGAwPs4X7eA==","shasum":"381ee8bd69964c84d65b356708daa8f8ee127a56","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn6nkglc.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbO6ECRA9TVsSAnZWagAAM6IP/jJz0PNUsrMbHiMNse7W\ngyaDZhnONLLL/UCvWGoNsDtYatpTFQ5HyayEC9Z1IMveqQ+Xbe71r4edSkLp\npsfI+i4hiWeWKMHRgMV9dEHVlRbYcIltMoFaAcJgIzAfTu2me74i1xVOvFXV\nsmIK41gOx6wNrXlS84RY3rHNJouRB5/FRWFBGTgbrCpHwfdQ+WQhe7TBoGvj\ntQaJ9KUpTVSC8IvUVr/5qs+xsE2l+oE8FfD3aTh6GQS7mALKGAtB0sVgF7f0\nbgUfiLOngU0bY5JJeABAmXdI2cGyytzvO0bA5yuoQwwhn/5WU8A0hi2wzvZU\nNL9KaHD3QLgRGl+Fr3Awb9TvOJR63Umb03eMF5jMWaeC1yLB2uLMBe1Eavf3\nhp79dpXnoqsjZe6fL2LguUZZxtSSfiQGTcEWyPNnUBiM4G39Z9ROXSzem9M7\noexXbc3VN0+LrasTSJVw19jK3EuTPtr4Is8D9zBp/QA/UsIUtmFmZntdCs0Z\n9QpT1Lh05yWeZTwU5sa2Ev+lh4oa1E8YG7mS+bXxkbl6MQeeD2LR6BQfqacm\nvPw+9gDCvfJBEcocOZvosdZeCk5aCG0aa+c0HuFqDucAegZKDepZatsVupmC\nTrdOprVmqCo1GXmFY1r0TZyV7CF/bZjIr3vuj8+B2aWU/AOjCpjoSsjAHqFm\nc2Pw\r\n=RGRQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFSXLPhzavaNY03hsz2jc5u1TGBirRAeoEtVocMRpjhNAiBLolTVYN/mWcI6gvY5vKBLvOudLnOpf8urYe/GFSZMOw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn6nkglc.0_1617751684220_0.6982434180417529"},"_hasShrinkwrap":false},"1.0.0-canary.kn6o0llk.0":{"name":"plumier","version":"1.0.0-canary.kn6o0llk.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kn6o0llk.0+9a957c9","@plumier/generic-controller":"1.0.0-canary.kn6o0llk.0+9a957c9","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9a957c96e6e3e3e2f7d6973902353d21c8035c7e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kn6o0llk.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (linux)","dist":{"integrity":"sha512-yNO42fQb9LQwsSZ7WBmWjgIt0APeDxCLcG8JlrxV3WFJspjYwoPT+KewAPLvLgvN+T93Zy9rdmeGYkHAAm3Kog==","shasum":"63bd1675e20012d43c2d8ed88f6510f84b9b0ec8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kn6o0llk.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbPF/CRA9TVsSAnZWagAABNsP/0B91HGHAvP+ag142GTt\nlO2DUoFBiRUWy5DlA0W5GbVGuRKvyKQ6Kimc/azhDLWxrZtYqkvY+B4bKmwo\n6RDWc2gx8Q23fLpAI+ByudnvAc9xCbvsyeoPzd2Q7DnAmhmSfT5mBTmxm3e1\nI8/lXqs0GG4dMD2v5mmkLOdKqZA66h3L2I9Qi/0SIeOQRFAgzyP8jv3qZKGI\nJ46j9Ta1rFDKM3q+ztm9TTZeCuifPavCftMEpX+WVMPEGtmixYKKlB35rc9B\n0NPw5wSvhUyShRykLW4v/uzpVyhz8rqUl9tVznXn6f76S+C8iQG1TjK2zUL3\nKmd1lChX/vZFCRzvDpJjoX4X63qxW7x/FgzxWGYQE5oEZ0T3HCsQaAm3vWRm\njRS8jFs6srgtes0GhKc1mKE1CvZxLdhd8EIBtMrugfoopsz+79T2S3c/EaN+\ny4Ow8gpgcQJZ5guh/IZzBRrzPUmupMQB9o1RfFcoZPLxIkPuSOGd+Fj+GRJw\nfcw8M5iFGWrh1NLYTJPX2dht4Zv70U0VKAzDz1lbKO/y6W/7InIuwJHusvTk\ncaXtE5RaaAdbXM8HfDaxjnhX4MYHYMA3cdayKi7uQaSN3ThDsYK4PCsstWEZ\n2+kOftnKpfi56pXDC8t/soEOaLeABbA+C46Fcoqb+TnhMC5Jrvnip0Q2kflb\n45DX\r\n=4HFt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpNhO/ND76TSHxJEUgIa13vrtP9v2j6oRPRVhC4CftFwIgXniQe8PiN7SEAlvz0CK9kU4yQziFM7sSTaI3tMJkPBM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kn6o0llk.0_1617752446845_0.30495878745313365"},"_hasShrinkwrap":false},"1.0.0-canary.knc8f5oa.0":{"name":"plumier","version":"1.0.0-canary.knc8f5oa.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knc8f5oa.0+1b75710","@plumier/generic-controller":"1.0.0-canary.knc8f5oa.0+1b75710","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1b757104c5cedad2264f2d88ad6b8f290d9d2130","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knc8f5oa.0","_nodeVersion":"12.22.0","_npmVersion":"lerna/4.0.0/node@v12.22.0+x64 (linux)","dist":{"integrity":"sha512-uNHrxVNTYSQzO16KXyVoJ78Zs+BwMuAaP6rqMi09CViF3lD/Y3cj+WHvN3UAbDlkJa1VD7ubPDhHVvbas7fwKQ==","shasum":"5ec5b7ef2fb1b264b36a43f4f01d4275bbbec83a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knc8f5oa.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgchROCRA9TVsSAnZWagAAhnEP/0pX/Fx52aIpZH01Z7Sr\nxqylwT2e78ik0G2nrvJFa0cjebjbVdzcC2zxEMOtut8ydQmD3RnqZUPi90TH\n7gl+adEvJLmB43YYkqmSS7/TpbdCm8weNAuN0dCA70AYKaZlbzAf+w63cz/q\nTxLM1Hv77LV1CeX5ANtbAF52VY1tRKoujefn8Ld4NwDdA3mBcF0GrJDDZjDp\ntDY5p+54md2KCQ61cf9U8zKeTArEaaa1EmvHPwho7Hvcvg3Ae1rxooIOp7HS\nyRv9M/Z/CfO3MdeQPha/cS1mtTvcwW9zDcgg9gCv4avhG1sTV4CpY/O9V0wG\npNcyVyGejbmTz1/rhSszoiL7xwlc5xI+8J2lV2sku4R6QC7ZcuXte7v43YkN\ntD6xMb55bz2/7ZJsuN/BsWO2k1RDNH/GNTTTT7I2/aigDg+oG+w6bWxpFnED\nIaXx0yR0Oyt2DO03qwzEDAKqQHoVxDytK+dFUVPrHbKNFLcM+JVgYb2RfwBW\noHVKvE7UydoT1mb4d1ypREAm70op7tv9OkVppWLnlceLAzbQBymjySIOeFde\nbLCMwJzdTK1mk6rCyCq6nWftbkEY3Br7N3bdmeX9Yqze4/exWtVs0+oPbyRv\nBmkEgn1sKVhaszLJ3u7wrhB0MNZJkxgSx/N8+BS8mcmsZOYv+QYA72ox0Aru\nVe/x\r\n=I25F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDPSd1FJ8ABRfN44IbVbT7TFoACVqPr2cIJ6noGjnn3lgIhAPyi5snlV74QX3r5v1DP6c+LOwGXWkiNbB+Ko9zMkPUU"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knc8f5oa.0_1618089038480_0.8644899565946789"},"_hasShrinkwrap":false},"1.0.0-canary.kndte9ap.0":{"name":"plumier","version":"1.0.0-canary.kndte9ap.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kndte9ap.0+f8fc39f","@plumier/generic-controller":"1.0.0-canary.kndte9ap.0+f8fc39f","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f8fc39f47b9e0fe3abaccb9c84e7745e7cfa758e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kndte9ap.0","_nodeVersion":"12.22.0","_npmVersion":"lerna/4.0.0/node@v12.22.0+x64 (linux)","dist":{"integrity":"sha512-mSKpkMJAVrIca0wz0b8GvITQ3BKXuWin6bhuA37rvkzU3Eq9moGvWtk5tIHjzhO8jY0szEJ7ASGNUCAtsmxU6A==","shasum":"d8b144f133a462237ef55bb521cd00c1f311a8e2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kndte9ap.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgc4ogCRA9TVsSAnZWagAAKRQQAJmw237haXIiQIsD1SFw\ngx+55fCLd+/EkkyxL3DCOfUFMl8HnGW6x8AyXhpVeqDTgyxQ2bLENpW9kidf\nUwEnGIehgYTlPJFSvwsM0EqyvsjR7hAdlmref3lFHYCt/6mQSlAMpMHnNE15\nmxeCBW9Kse/VA3n1T3MVcuCo9+fSNKcFxFtPGdAfL4Yoi6oqIxqMYJR/hnYl\n8Zrvo5vCgNGKTxP1teHBDml4Wsy21+/35UCJDyZ9tiY9IB4/kcUrOR87Dgh/\nH6YU8SoXdOBrzhN5NGfcrFOxuM3iNiCGaDgXAbPukxVhL2So6K/utfx02I0e\nIhz0aehX7andY3tWUV2Pg3+yEyjDg6Ven9TLwSjA1UMocCZEHDxuzqcBMbOF\ni6vxNhKOvzqZd62v7IOAvnl2zJdI96oTPzl2WNMQV81jvwW2sgTgGZhDkjZW\nP9iHBUbRx5mhdu4IHe8JA0dgRq0698oIiWYNjyo9/Dlpx7s4Lamu4j2nSnfk\nVfvnWXC8xDWbqIilWjHY0ml2TEMc0NdyjlfxTb4eVHqUtXhSL4JxqXjq++Jt\nuhr7FdbP0Qig/WT5WScWQZafll2QSgnMFcPrKgmHt1pR+b/uaxgBcWVjnGMu\n20Vcd4pgtrPX5Uuq/Dt7B3PlHTahWMc2G3LysDBNZOGVJHPfy+7kBEr7x89t\nTAJW\r\n=fRYS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCM0i3ASpfmShNWktF4d6GaCNmbtnzBzFxTCe+3jYX/7gIhAPz3WgANJsDtgXyTh9t21Ey1vkI8Pbx2jISio03M+arh"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kndte9ap.0_1618184735611_0.7172110830363396"},"_hasShrinkwrap":false},"1.0.0-canary.knee3yzp.0":{"name":"plumier","version":"1.0.0-canary.knee3yzp.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knee3yzp.0+cd3f188","@plumier/generic-controller":"1.0.0-canary.knee3yzp.0+cd3f188","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cd3f18881f0ab106462727c4593915f086dd823e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knee3yzp.0","_nodeVersion":"12.22.0","_npmVersion":"lerna/4.0.0/node@v12.22.0+x64 (linux)","dist":{"integrity":"sha512-0m/3KvoOFnbqqET8EEQWy497zfqNNsrPK+MtVBMf7DH3inoUhGRUbWgalSC77oo7Cf1gijKEZm+xjVTdukimkQ==","shasum":"692e1c243856c81cfa2a5882ce03f28989f61328","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knee3yzp.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdBIPCRA9TVsSAnZWagAA/owP/iocsxMMdhQLfU/SnIdQ\niVDiUIm12tnwXmqBMj9QLQRpoWhAFofXUqwtaczl6sQ2Wpqs+gkcC9TwGRwC\nUByQ+d8CXnqrmxSJhNVUlZa0ctztIP6GR2UKOpW1YiJGimzv9S9lD9iOLi/l\nRkjI4MiGFf/UmLKove+PriF7bJr+EazU9zdEzFL01O1E4tNIsssCO5BwkWx6\nTRRd9CcbbS6Z1vjFrhDorJHqhyj8ezzckkYssO+ZiyHm6uztQ11oTAEtZwMf\n1lvZFxt3GX9aR39avpoQW409kXl5aTC3tXoYgoIJ+3NxHFo2E1hqik0dkCZD\ndM2AnZCQS/tlDBNm2f1QMEpZeYLL6XLE2rZfYk1m3iS9SGWpNiS3t3OlMk4s\n63l0NRudmnxLS0I/fNnDPrzp32UICFamggsimq3MGF/HkuPD+Q+TOyg+kM0L\nbA0NKwXWaTnZVoQ7ZYMgCNUiRZoC2l+Aegrek1uR+gAZS5brXwjK31QqUD5M\nvDSbYobbqZ6pB1qYveyh1DVF01TnjfeXehJ5NdpGklf6rGpVWijnUEPnsYPq\n76CUvTkCM6vrrz+v9vKHOMd75jF7D6VVPDXW4kCeQWwqLIJD+9fNw4lZDFcq\nZvJ6RLZHwB2Kr1ZPMmjJGwIdV3USJuZVXQOiLV1aGJZgXEJrVyVNJHb242pv\npHVp\r\n=46Y2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDvrF2gU/FM/r/1fr8IF8lrCTk6x2MzEabmHR2/Meso3AiEArG0Gfm8TQbguyDKuWUhLvJEpxsB33iozEty8oHMKP88="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knee3yzp.0_1618219535201_0.3261825687285951"},"_hasShrinkwrap":false},"1.0.0-canary.knf85b5d.0":{"name":"plumier","version":"1.0.0-canary.knf85b5d.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knf85b5d.0+dad95ad","@plumier/generic-controller":"1.0.0-canary.knf85b5d.0+dad95ad","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"dad95ad8e250805fa7aa6a9eb83c6c03a30e4414","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knf85b5d.0","_nodeVersion":"12.22.0","_npmVersion":"lerna/4.0.0/node@v12.22.0+x64 (linux)","dist":{"integrity":"sha512-RT8oFD9gIdkTgwWTOxi2cZqp28jPJNeI5e3WGib/n/e/Avxiv5IVNNQV+tVHFFa/c+RUijr0jGtHPkPZKth5eA==","shasum":"454feed1048183ec4f68dd5b93025170c38572b4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knf85b5d.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdNcbCRA9TVsSAnZWagAA2YEP/A3Je9HquNmyA7R38TS5\ncQTMmiiC3Xyxa/AH9N1LDMwA9O1TlK01qPEsaS62ZPjHASBCQgZsTZyUrlTG\nCA/D9AUikJzMMEfUkGAJlUj/6uo6JTxcUrxov18WaH+/vohkifm7h0dGX+NH\n3ZsuP1MwJ5Oq7xeHqcRGezJwbJp492+E2GjSJVr0uEiLLa37Qh9DtI+l8Wpd\nbsub0q3CvZcnzEvNuUSZkI2HewNph0fuiYB44HaKKr1+vKqOi2nMvlOErKLe\n2tMkMpyOkQ9D2HeGw7RmJy+bGSIuWZXlCiKTQsrnthHhOizrawZxgFlVts0g\n8hx6WJ9RiQrZcQKrXCdxRNq8PYjsHCLSHfOSPdSwm5gSAGihTtI2FXUc0whI\n/BXPb0HEgf3KpDy4ZbitHX9pFip8LHVFV/oBQ6nPKGG1vHttqPR6MdniCOZe\nUZA9muJ14AtlOfF/O3BEO1EQZSUr9QjuBVDqZGi/gSLtCFF1j2g4LzTNAmKf\nT/aI1wuaUWDgBlEWFw18FKW4e1b7JwARTr8Vd1yvLa1iZ7xvsl5pYSQpEb7P\nBOtqaH1F33UJ4kY7Pr3AsyvVO/U3HN7t2IQ6uwOIyQMkRrR4G3UeYsnrC3i0\nr8KvbHvYjLJH6xo/RFc1YZ+EBnZ8yrhUoyBl/TUziduBxsGRYPqCXYpnLqj1\nOge+\r\n=NcIW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBPbhPJ6E7zVI9xsspZgrJqx4h1rzhB2qihf81OhPaQeAiEAw5UXMdTVTbXqdsO8kVs63jVAj0GprDQbXGJrWFPh3m0="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knf85b5d.0_1618269979032_0.8108295368718046"},"_hasShrinkwrap":false},"1.0.0-canary.knf9jw7j.0":{"name":"plumier","version":"1.0.0-canary.knf9jw7j.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knf9jw7j.0+5e8f3b3","@plumier/generic-controller":"1.0.0-canary.knf9jw7j.0+5e8f3b3","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5e8f3b3777fd6c4dcd598b536886c9f288308963","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knf9jw7j.0","_nodeVersion":"12.22.0","_npmVersion":"lerna/4.0.0/node@v12.22.0+x64 (linux)","dist":{"integrity":"sha512-GZeocHy5alo1gjeY4d/Ed3zmqAIT95E5uDJbvUqEwEqFcyRHtQi8T8NGSZC5hEHCQLRiG8kzznHqeXsKzOQI8Q==","shasum":"43028d42d41d7967ef345436deec8b9e2d7e6161","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knf9jw7j.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdOBcCRA9TVsSAnZWagAAajkP/1uIOIyzNoQeLLZ7ITgf\nxksziYh6aOeZneXDxLxwfkqGG9ZcXGZdTNKyMMTbzj2x25QZdZ/R+9GKAfTR\nyeckIkKkpRWVgiHcCIUL4+1BXFndsu7Vc1audEOOOeiXgHUzRdOUqqiY8ZY1\nD3w0Csx/PEaKlClAeJM1BNUv06h/18rvCzSFL6m23eXTBboF/wOauLRwe3+R\nN49HYtdwmzcTByEvIwX7f40sQlKKnchzuvTmUhOw8EYfPtkjeQmrzaZAhRA9\nKP7jeL1qXxHZ4KneWop+jtiJPOhasgG9vOmFZ6ST3VVaOuIH7CpfaDFRHt8P\nRhD/texCjXKR0WerqE7PG7l0lxitj1LjoimuwB2pasrtXP6/cugPY17iWbsn\nH6Of7NMvVUpS4kYFe08aZgHwZv9eZ6Zhxy7iT0SHhURa5V0soagYtI2WMsaE\nFdLp4JE2bx0vXh57MvPIH4RYMVl3HFtGSUV2kykdfTl2pqqJ9x2nV3sXrpqj\nQd/gyBiHCaP2fMv1Sez5w6sgkEi1XGoUWp+F+irbrFAX2VrmJ8brU6A0G8W9\nZFAsRyJYYvdT3NpmisjB4FU3dbv2bCGBWDCwczkQUFL+CH+MPEWx9i/1h5xv\nU8Oj02V9XdTpK+kfMmiMBQOEVPRnyeUw0dsfPptWv73EBRJQhweEod47gxEZ\nVcVQ\r\n=GSSp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLpyPC9eBoa5I3NQ5oocKdA4GBLqx/dYLKKsjVZGYhsQIhAMvR8Nt6wpyWCFuMmB3WUqLLnBHzX5fdn/jVEoLVHq1U"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knf9jw7j.0_1618272348423_0.8191089588284131"},"_hasShrinkwrap":false},"1.0.0-canary.knh8sx6p.0":{"name":"plumier","version":"1.0.0-canary.knh8sx6p.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knh8sx6p.0+1b2c9e6","@plumier/generic-controller":"1.0.0-canary.knh8sx6p.0+1b2c9e6","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1b2c9e619889fcb055fe714cde4995a0244b72e0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knh8sx6p.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-CaC+4Lrzv4sI3W0lR4oGD+igGDt8RURC7xIf/3tG5N2cyIU5WJth5dNWtB1bqpiEE9jIk8BvlvCOzsW/fjLw7w==","shasum":"d1a70ac47e91782363bae97b1ac5158e29125934","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knh8sx6p.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdrPNCRA9TVsSAnZWagAAArsP/2m/iqJuSDj2+eFfrPSG\nH2+2ACqLKwYApUuV9z24B6+nm+AMRseXz1OLSNhtfc9Ng6M5WeDDM7R9FvxW\nj3342gRpTdK8Mu+I5hDvpKxPMmvPf593KC1FUkLtnPphrbX17GJXc55xNJC4\nZocKcWUgydPE9Wn3rcfV5900Yry0YNItUj+gbXorVCEC7wFhePV3rBd7fEiW\npVa6kIvhfglE7sGY0+1o3XEYNaBypyC2K4ptz4DafJDL/ax4zCLk+cSLc6cm\nuHySHNGVS4yZqolgrsH/E4Ks5NsKyxz7nOX37SWlPobD2O7APnEN6Zd4AvB/\ncSsAU7id7xa8+KuW7F11e7bfqZw3WjTA9+dewCDnlPFt+dKVH3Iv5ieh212L\nOsltc6UV2d+bz/aHlPozh9zrn64OAe3OtZBaA8RUR+sRzJY9ajWYP6kfPYRB\nbcyfAaZba/e+OGIl8By5hseTpiRUDhiGvIQu1grLhrTzn7joG3TQdFx5Gr/2\n+M6Tv5nw2E9BBDGhyeCKZgR4X8LFsKNgZSNW17oBGTlP4y0kKQr1x7Hptaqc\nRdavYBM9OGIwH97paaAylRiTNOzMHHw1CaakrOLnxHW0sv336NHA26IRsVf6\nrMGbpBW7DxH7bfDfkCXRdHfcJq3LqZP0DGYexUJTuQWN6ohkbdVCNSJxpFBV\nIqrf\r\n=LyTV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA7ZqOh3CYRuV34fTPUzXRbP1PedYPepxPJQv2vjI+GmAiEA0Q04+fLXC5e+AMgnCNEQ/K/zQjIf1owT+QRbWbmjNH4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knh8sx6p.0_1618392013388_0.44354047765986326"},"_hasShrinkwrap":false},"1.0.0-canary.knhwgudr.0":{"name":"plumier","version":"1.0.0-canary.knhwgudr.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knhwgudr.0+61e8f6c","@plumier/generic-controller":"1.0.0-canary.knhwgudr.0+61e8f6c","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"61e8f6cf57710ccc24239ff0369c38f7410f1b54","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knhwgudr.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-FKDnXFJCKN+nHJevlGn1107EdnG0LSKQhAGf251MCykqA8ln9nNscjlL1udpPB/XdHCt8SpE1iz7KXDsyYRrHA==","shasum":"3a62b5a142bb5833738e090874a99b5fb7c96ca7","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knhwgudr.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd08RCRA9TVsSAnZWagAAQkkP/iovYEtq1yfZQrzYdT1v\nrkTDs6jBYCoy49dwQ6jhsF0YEXUgbzFEUDBwaLnem4Ffh1YEgnZNJcSXAIk0\nrgnQEvvIPXBFY/LyKIUMhP7iB8+82EyvXZNg+Ni9AV+nc69lgpimfP7lCgGA\nh4HaxyWIGLWpNz4EeGMFSYJNNATOdZUM0XyG7//PvZiXxTppVC9uYzI/x7k9\nKjNLCELB/pnIphROouvL/rfK6DBxUvnBmoJaGEv1xMyWiA+/FdNhsrCLAZzp\npTMCuGSt9c1oHclPF/mjXnfHKaVGw8TCfx7AuJR8lO7wIkkIY+j9WSYVUECN\nefDs9Bv1IuzVCmXJOHnMgTXJON4vhBtMPj44iUDNn+9u6fFc9JtSukEhdW6U\nHSZC9UomOb/OhJaHWKQSvkp780bjGpVTNxB0uubnP/sjQrdbLVyDdejk7NtN\nJrI/rQ1yPJZ3YnRnz5r37EayJh7aJ0Hm/0MYc3eAVd3RRhd5MSkH3iJdZ9QP\nFbtKzsEPdzZW5Je4nHA1gv7vV8BCUgDUnONkixrTp7Ngbuh9RIQyViDl9xYm\nyiE63cEiG5F2lh9SpjbyrBzMOee+kan+j0xBCe0obxrpyZcZs17XI6hkNANs\nNZAmUZpULPNKjIuxMdMHNS3VTE+crmy7iFaEzrx022Z9+0ALBMHrtDVNiuXV\nkv8e\r\n=2wVl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBawtPn4UNyiG/b84SRZbRhdYbUqyL2XnQrf68jacnG0AiByUf0XlI+UKF9mCNXgtegtt9XOqeUXil/8Iq29gAWImQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knhwgudr.0_1618431761376_0.5025570108363477"},"_hasShrinkwrap":false},"1.0.0-canary.knhx180i.0":{"name":"plumier","version":"1.0.0-canary.knhx180i.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knhx180i.0+c86b5cb","@plumier/generic-controller":"1.0.0-canary.knhx180i.0+c86b5cb","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c86b5cb3a114aa98646e4fd183eb09423ba01586","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knhx180i.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-rYy4ye1MzPl5kyqq5C2yE0porv5VOR4+ZPg5A3RTNnwZf/sttMa0XX3AYOCBIlU36Z8CiADMe34L1JhvHn/XcA==","shasum":"e8dc35d1292fd127f38f0d611315fcac817c801a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knhx180i.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd1LVCRA9TVsSAnZWagAAHFMP/1y/gxrOnAklikYhVRwI\nwGmXVOiNFpLOXF26uvK8sV1BcccDwsu+NVcfyAD+d7C/TBdP99oLdm0KRit2\n8Be1zus9yZo7cUBj3HqLz+gPgPWRZnqQXQNon6I4TGhwKSjpySjSSA4m3ZWh\njUDdHRJeTMyDlCpzuAvtHu3W+H4qbriY3bEocmGEQBra2nff5q9ueAkdnTTv\nRLWEfgUxmJobMq+AKNTErCuxg5IzroebFEfnkvO+G7jNEYxwlZpTvpHgb++K\nvGVrwJMltyMDeo1g9+S81FvYOxJQPR9WjS1l4kYO6cPFlKWCycfpIuBgn/qp\nxcH6Y6m6gyS7GewFh5KrmhuCXRbVR3Yavzw/wut7oBws3LnJsUHTrv0M9IJ3\n8FEaJ7tXYRxJwF+WAuhIP8jYO6mUIMf751io/yxsqZ3WrTN6kohqGlc5CtBL\n/QuYqSbWJTTm+seQ5nAFVcCUmad+6VuOagJxiOyWi91za4caTCqShFVGgNeS\nJPqBv3zrriBkobJDCtb1Pwb5aqLmAyMDIArhVHYyVozELS49yr8gTeOw5a6M\nLUGY/r4XCwZPNa2J9A/1T7RsnGU9bxl5R1YoQArdZjqE6My9u9+VaGCm5BsQ\nZ978jQQHD2g6nL2Dse6hsqYRHixa00LfMS4KZGY2pl6sjanD/Wv/uQ+bzulU\nj0U/\r\n=MiS9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA6o/k07Nk4T2mIADinEvZqAwUReIJWVpfkAJX+9nqZDAiAtgTGek+QuiKhXqMp7yhkTTnhNd8mPqyHZF3SgBMz+PQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knhx180i.0_1618432725359_0.9047820655878647"},"_hasShrinkwrap":false},"1.0.0-canary.knhxsgj2.0":{"name":"plumier","version":"1.0.0-canary.knhxsgj2.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knhxsgj2.0+585c61a","@plumier/generic-controller":"1.0.0-canary.knhxsgj2.0+585c61a","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"585c61a61a7784ad0f13b2a50359f31d9bc36ffd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knhxsgj2.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-GowcFuPOxLUdenmDaEUYhCtiHNkaaRKOr2+KkEwFTty2d+gp0HBRFOdek6w9gd1iLrEjjd03pCtBBD7tVlNslA==","shasum":"6a85038b8cdc0e57c98983000200da34b580c278","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knhxsgj2.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd1fACRA9TVsSAnZWagAAzTUP/3SI+RJHY9fPZvxQ/V9T\nPkBeH+O9XhJFJTiFXVkv+LJ1Uzbwq46H5602/YDoXsyGog6FEfR5qucRq49L\nQFGrTSBL7gKQ+HKQvLqOR7bkCD6COvLvP8Maufont8vJ3F4gJgl1ZrPQMv8h\naTAWV5X3bjVz4OfKhnkSEDMNcH0+vX65fTR1ia6+i16UyOpaJf7UBGQZILyW\nsXIBs/yBlfCRgFWqvLO0WaiThtI2Vdc/TT6/c4ITUs5phC9ME1hS9qwWjAMv\niyospp1Yvn5JKMnsq8ux/xH5NNsZuE5Vjfy4C1FAMV1qrV/cBZFU/boa5esx\nQ6+sukMANfXyiDGKGTC4tH51vnjB3kMdsqmJY5vJ4sJEeeG9l1gEkz5IlpC9\nuZmQ0LVstAZMbm8fr397j1teHkFp3WiZODnNarH0Jgv7v+mwaNjGrCzVPQQ4\nWO8Tt6uX3kqjsUnBiADvOFWtnuATVnt5NaXk7+aDP3kckyRcEgq4Mvoofo/h\nCj41/C5XnRcussQTP18rRwc0f1f6DjFXqEA7W1Em68eWdl+OW0cvq1/RT7PQ\nyeBWSHz393RwspExWpW69PymKM6rd+iPXSQQqJFehvGWi8cH8+hMZsWgjO0+\n9WX4PWbT+XAcNcdB59qz9Pgut2KQGNAx27ldEFBgnQpbzNnJyYWFsVHpb4r6\nMM6D\r\n=dcGD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGbI92vxbgZHzk0JYbFYP587ZTuu1p7i5XXHBMtk5xxyAiEAyK0ID6LyYp+e0KCpV2HE3Pk6xqnr6mqc62DDMUNuEUc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knhxsgj2.0_1618433984004_0.3825104883017387"},"_hasShrinkwrap":false},"1.0.0-rc.11":{"name":"plumier","version":"1.0.0-rc.11","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.11","@plumier/generic-controller":"1.0.0-rc.11","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c0486358f6e3ba0405847ed2f277735c58ad48f6","_id":"plumier@1.0.0-rc.11","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (darwin)","dist":{"integrity":"sha512-vKqIy3Yp+WwNT9cxHW03MAD+1wcSSEs7lBpVVDTLWWDOmzBfFxsEWOVJRGBgnx2HPTlcBd5OXUSvxgmN7JDvJg==","shasum":"8758bd514c468a73fe2ce97c3fc5db1ced07e990","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.11.tgz","fileCount":13,"unpackedSize":27856,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd1kpCRA9TVsSAnZWagAAczIP/2aS8CQQzJgjQVz50dh3\nontKQTsI/UynXIYOQ4CaXjgN61zbNSGdncVxvCyAPiCI7RWXxT7HeEKeE7Hv\n33Rz+3YGRxSPYplf4+SLLLYL1GNCSCWJCfQNG40oWwAajTQDrT64Eo4svn3A\ngSCtN1KZ4sMcvgFeOM6MVmLfXAlgCJdMlvNrxtq29Husi6mEZKTiRc3F60hm\nU5ikQfsgG7e+Lb2pO3QAD/4tX3phgXJHhJPQu8WwHpG3SgNGnWgX1LZhsHsy\nKX9wbh54yk1uxOnc/KxNffd5yEXQ0f7Ne9SkzBjhxGbW6Lyf1GmZJcyGtif0\naXA2WybsnndG7jZ2QXGRSOJHx1cfEZ9bxkIsBXAn4jUkeLB+MBUtJ6lMVn+n\nbuguH56wwbwlbvIZVN1hf5aiWzA+k22LDBOl/2ylutjwHZj2S8AqCTzXDauH\n/3H9tIgvwo8RYiVZ7Wq4t6RkOzwWXRu6mxqrqleJ+r2GytXYdfo53ImCe89X\n1swFvuAqFP8dOnYQPvS6ZmXzXSR4IwIOcfdhlRfM2atUP1S+xOCpuKGWPeMO\nXWXXJGKA9iQCnelGhIzMqqAVhGRKso4tXwMrU46pCbfrguhxHJxBX3umnfNU\neOoBkNkLcFo2wkIsNi+fSTu8j1j4QLrrsEXBF9F9GvaHH89JkwPK2Wk9rqZ+\n7vRM\r\n=YRxu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIApSlnKTZY6TsuXtjzTXbbQHGjzwO9H9Vri4KzzAqXFqAiA/9LWDfdcOnvM0vIJQWrJLDZbFdf7r8/Ao0DyQkcXmfg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.11_1618434344945_0.35331166243286294"},"_hasShrinkwrap":false},"1.0.0-canary.knhy71n1.0":{"name":"plumier","version":"1.0.0-canary.knhy71n1.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knhy71n1.0+c048635","@plumier/generic-controller":"1.0.0-canary.knhy71n1.0+c048635","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c0486358f6e3ba0405847ed2f277735c58ad48f6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knhy71n1.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-C5UevwTOvR/coAdfInYBpYibHkiG1hQmzo6KdKJVPmGnepmWRqCxsjmKfNDLqZH9L4mGzkLJFeioNDZsKljALA==","shasum":"486233391f63e5a3a1d9142fa4f4f80c4d947be2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knhy71n1.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd1ppCRA9TVsSAnZWagAAGAYP/2FvlLomqNiMoKzU8HQA\ntGBDJFWsV/iQ9bbASYDo5lD8itPb7xc5DiLexsiY9lp2VW6otGbhMOtyY+W2\nhixrBvTWBvjnhWkb3/KSffDjKgKKaR22gWdOpfOosqOCLrp3wr19NZBBwFxp\nyMKiQiUclARDEK32ub4Ke7RPdrE+GnTfFUcNQlMRIWYvJptvTQSAzGTD1i9u\nRMT77SGI84j/wbh0w7rvyo8xpN41/7W4/DAS/E4JAQ/P6mSTozzDYZaiB6ym\nme9ypDl1m6eTfgV8VJAnjfI8JyhNx9HSh04EPqxWnMxDW1SZ18U7WkUUofKn\nxu6Akhiur8NPiiruO6rnIZxQFcNrO7aNiNVWxLBtgFZlJAUoag1BivPH/SEO\nlqlcH0aFIRqVEW60yqD9LDukJ/Fw21HCFDfuy9XF3DPuSuNvEkXSYGf0pGzq\n3h/MNaIRcrfn4PYO8cel3E5w4MCI2plUZ50huWn9DTS8i3BcYlS/8M+toR5X\nbBwZrOwwbfetHxDqb3inhtVeq3MwkMs+x3PivH3ueqZvbReXpFIc9dU0IIOR\nfD4sKf90+3vUXrE4KvY8GpHC5RLp6RK5/YhoYdjTScTk87i1dKMZXipuGlOY\nDC4YuG4LkIc6zdO1BKlsS3Ne2kH0MknihTJuXWZZt5rOBUTUlJa49x0jAdve\nWSpt\r\n=TRjV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/9tpwYMwxlGeHmxPGSZjEnyvyyh2tvyxSuz8iz2aHCAIhAOKFzm7Wp9Bek2471xa9WWniZHHQC0fBgwlBrLwl2Frq"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knhy71n1.0_1618434664688_0.4935134674294519"},"_hasShrinkwrap":false},"1.0.0-canary.knhyqnl5.0":{"name":"plumier","version":"1.0.0-canary.knhyqnl5.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knhyqnl5.0+c9b6f01","@plumier/generic-controller":"1.0.0-canary.knhyqnl5.0+c9b6f01","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c9b6f0175097175a70959d1baec64bba9eff063d","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knhyqnl5.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-TK8YFL6AUnZRa7rQ9lmwDAuOSXvnFvZg62EFtYga07Odu/A5lcW+LtpNbPTSqKv6CMOoErQF+/P1OJ1Zj4Qvww==","shasum":"696f0f5bb95abfe1a763fa9975862a9671ef2de8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knhyqnl5.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd13+CRA9TVsSAnZWagAAm6UP/AxUS2QJQRajY7XO3/Xu\n5RV3o+eaBpyCUKRLDqyT7nDvTB0HLt2fWFjY2L3kG9187m45ukN169CHThmr\nx56gkiDLDFltFVZ36Q853fKZFGz08OtGf62oiaNZc6xIpxNBYRouFqrmUUyW\nDXdhBQEeBmcG1BjdWrkrRJEJjh0ImMrqsNmfl7uYTj+pt26D8+T3zeitb6H0\nYQy+skZkggylnUHQs8aIMLEI3hkarnVwLS7wNHGSTU//zbYmeoU3T4iGMjpX\npTApVVm8gRNk1e04PahoNzNamhohTqwKpJOJPbC0N6n6yOdDxv6UmCcYtVyn\npEGN2YE1M5uVXAfSf76AVZ+mGQ0AVLfe6fELMnOvYzL3o0GNFf5B7giSAiLK\nI3osApeA8WEBh0wlM/0ulKgeX50WmOxSj1WeVt6xA6Hj6G0fP0OIi6g5ze8I\nfnUuCE+kctlpIg/ke36NAUrF5ltbNJcV0qZrbCOLRBkhhqv5sHiob+pWSi4C\nI1ZJphMYbKyg9SayWLioCxydXIICUxh1TKO80RA6XQ7YTjfj3GiIrSiMnUYb\n63NlOfRe4F/hYSI6Zor1iC+B8Qv9lj+YD4Ng0AHVqOdvRZR9J3NyehvMKxZD\nvLDHKzZpIGC5qO1dHJygNXPgnAOF0CxYgktdIrLwJcJdywkDCa6vd1kF3/MN\nge5z\r\n=emhi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICHSLM5Ixxi3dxrOm9H/GVZybOec6lybq5iBkQLg+5rVAiEA+L41UYmfI5P3fOfZ8fp6v9MZmth2IEW8B4aCITClPZE="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knhyqnl5.0_1618435582227_0.9494982548542785"},"_hasShrinkwrap":false},"1.0.0-canary.knkt86ec.0":{"name":"plumier","version":"1.0.0-canary.knkt86ec.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knkt86ec.0+cdc4798","@plumier/generic-controller":"1.0.0-canary.knkt86ec.0+cdc4798","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cdc4798cc9f9ff48977e251c4ea2dbe1d5493d9b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knkt86ec.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-+JybbK6SzdoWZCV64oTqMbryf5D8ZHr6fb2wM2v7oZZBMq5lK7cSajlSEEZJwaL7foM9LcPsAo0nUcboR1dwyg==","shasum":"c51af6ba4d1b2ac79b50e909268cbb35b444c8cc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knkt86ec.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgef5nCRA9TVsSAnZWagAASWUP/Ap5xmQ2yVxAqMAlqn2f\n6oZiQ7AcIpokUUTMVPNr+Q0Nmx5TgqzLgfwHOwO70tPLnYvWDgqLr9/h669M\nUsCCTYIY2uOUU7D6lm4XrKGlxsTS5lDOMOowu6MVYkCvZaQ4shU1uXk6z8Ru\n6ZYZPxkslLXtOxuCfvwncRS34x1Kn/z8Pfh9AcaF4x0WNCwa/rQa/9fvBv3H\nBWPy9XPUVFJIN88NELOa+cJk8M9Gna56QqqomqgASiSt1HuxC5oL8Kk//Jmh\nYWSOueaoKxdwaMfhkE1L/xOBg2UZXzMFMpnWQAeG0DElA823bkgumlcN/Nx+\neigyQy3WFVMqrA2bzX4IKHNfFWyKwbBROzfNQODJC4Mlt79tRuRcXfz5gHmG\nZ9QlugllMtfh1y1qFQLv2vG/dhtrcamgS2JIfonlAHyNcU/QB9bYW3Ypzreo\nJvPbxhochz2xyxfBOkZQD417hR+JrAkjhvV7vBhteiSIA6SpllngnLbHlSuF\n0ctbnFTOysSONUpr0JMbAbqHzCkMfylcsHNFTiGYHrVdWsHqR2YLSrh2C4ng\nXrffKcHTZ3pvzM3W1s0BGfAX5MT0/hGPM0r9fIOnI3dZlHjDCrIOHaWQ3BaO\npqN6rgIlAkwJI/5GjrcfXyUaInqrRcUKj2PBUyvEdSDSalVrrQA97ZgMz5hx\nmuQl\r\n=kn+z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQClEnfOgAIztjzSqh5An7RRVdxJipU1VTIktm1iOBf7BQIgMxf38nQyVzXJUMPYH4+QzS+7fOs/Xgysxqt7g3hZFOc="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knkt86ec.0_1618607718934_0.3136890915034787"},"_hasShrinkwrap":false},"1.0.0-canary.knkv8u23.0":{"name":"plumier","version":"1.0.0-canary.knkv8u23.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knkv8u23.0+1f45c57","@plumier/generic-controller":"1.0.0-canary.knkv8u23.0+1f45c57","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1f45c5793eb3c78992c42cc20da6f0be616303d2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knkv8u23.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-KpUpHx9L764VL8BFd9OwUJ4xys6J8OVfJMeQQEhkOhyJju2hK5kC6NocvE1QMnE3APcEw7LlEsy8p0So3UqTIQ==","shasum":"7d608bebfda79b094fd23238b74402bb7cc1e684","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knkv8u23.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgegulCRA9TVsSAnZWagAAN8QP/01MTQSgkSIH359kkDJT\nDZqscTIOkxYwlJrymHoOfjw0V/8SME4S6rvKjH91n/I22JnvYJe7Nr682/H4\nlTl/xCuFHM9pA5hLzPVEsVB/cvw0Yda9EouDlFcyG3nWvGrJQxnmcPe5FK/n\njT67IPqwy0Qa6wQ1Jy7DgV8QWmXw6uejRgAF5dO7gRitLMPAcwB3OEc0jY9r\nfvr7XqOabr17n+N0bnfURGNFfFb02yYGwpfZ5988xWKmZwk++hb1FqqY5HJa\nXrBw4/MkrWIO7WLjr1ejvu9rXiaS4qpN3m9PPuNN8PCE5WVJj63M7TcDzeVF\nm93FeSCaLOmCw612bKz+Yn1SStgSJjWNmTDPDRZS5OReoiz9i7Nfjw0tfhdm\nZymZXrPGoqW09jG9KH3hBkvusL1anuLUFECWvI8PooklUkfgjARsjHagze+p\nkgJzV9cdievK+KYFk32s7bQh6AGwSi+F2AnC2mZhvLMk7FGtDuiOCFH+6Dde\nQcZifjSVRUy67HWDAGEBRtKy7yY978vr98yhFSEAEcWzWukGdXotDv0UgGdo\nr1BCvv3txKBBT1M1KKy/XOuO/G6zj3D3r0EDqT8jTmWeTvj6Dv9IIa103F8F\n3ToLuNKBJ6X41Pkd0gx0bCrcjWpMv7fRXlYgceJMVAmFZ1AbOZ9ygmfTp/R7\noDSp\r\n=r8nT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCK3CQ4FJVG5TNl51HivZE/zH3ZM2vFu6wtyHxqDRCxQAIhAJRoEiLznO6uOuaHAX2wobtswp0a9I9e4j45uZR/bu7T"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knkv8u23.0_1618611109237_0.42891661480425136"},"_hasShrinkwrap":false},"1.0.0-canary.knp0yxc3.0":{"name":"plumier","version":"1.0.0-canary.knp0yxc3.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knp0yxc3.0+df180dc","@plumier/generic-controller":"1.0.0-canary.knp0yxc3.0+df180dc","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"df180dc9e3355b6f3a23e6f7787b9c14ff3821b0","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knp0yxc3.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-htDb5lPoodpJcjoRwCKn2cp5lqYGpdIBt6jXziSQM3Ee+K+76CYy0F7QI/e7KtIvP/VoN4Ihgg87HaqKd2Y/hA==","shasum":"991992f7789beffe0a871774428faead38bf4393","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knp0yxc3.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfeIBCRA9TVsSAnZWagAAym4QAKLeE6eKhebmqYzATw+n\nYq621FLTZ67+c4dAoB56pRdaAxSJnhU6/vz3efyfmRV3JcXDfw+0FmRVvCd/\n/SrzpsKSOtAWElA8DUNGcM6RJjqgPkwNzyhmTtOvWpFtyHzQHMLOq6SllERV\naCPUfdD+Vn+92XEtowxUGmkwN835Dx7yZK9JSEq9GE2L4hPb/tpH7kqKHKZw\n92ytIVXpB2yYizpDFzFKt1mW+U3qXNK63q1EFyGMUB90cdRzbtIWm5Oj6G/l\nX/8qNDHGsIYo+gcr6FsxR5qHoujBbo3sSWLrP5ALaq3gRg2vQjExaobCy5Za\nxZLGLphvVq8jB/sOZ8HA6Zih/wtC5N2l+UqXWN+YT8R2eWC8HFkz/QC1xLW8\nJG3Z6IeyiL++MjlmpdcCJB2kJi4AS3vdImzIyOlpYMNbwA9U1rEEAymI8r4U\nNji/dsapYY0UrbI5h02OeExyMGuBv5IAfYgLkjasl7HsiGBFY9HEVhfEBDq8\nyMyDuQ1UBRfsVbvnYC+0FbLqaEOpV3bBg4pJkXpEN1x9Xdp1b4vurWC1KDua\nccl4C1OnPIP2nF0Kb/sEsaIi29Zt33MTCPup3RSdV+O54qt94YnpuuZ6MV9+\ntovLZoIe0pphc/OxloFWcgImhigJJeMajyLNYAI3yFTleYv4hOCsw4N4OOiH\nlNQa\r\n=prKV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCUDqKghHitD1hf2AKmyveSg3SGK5O7HiWv+ALgDn4NeQIhAKyMup0Ex6GS1MvILCgbwEoQQqRlQ4dD3+3lTBWMRane"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knp0yxc3.0_1618862590858_0.8657768102686558"},"_hasShrinkwrap":false},"1.0.0-canary.knp1j82w.0":{"name":"plumier","version":"1.0.0-canary.knp1j82w.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knp1j82w.0+c8ab137","@plumier/generic-controller":"1.0.0-canary.knp1j82w.0+c8ab137","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c8ab137076ab40a9916277382623e99bf67fcba4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knp1j82w.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-U6mzzGxuhuAmvNeTJZbsYc/Of960qlshgtvl1dNtq/zVuupSf4kr0WlxYzbxir9uGI+1ffGvBY/b8E9G0ewdbw==","shasum":"94bd2ba128715c90630308d61323d3837819c57e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knp1j82w.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfeWvCRA9TVsSAnZWagAA5nwQAKLwn+/WZtLtGvhExj5y\nDFnrxDnglCM/4Rymz2FPJINbxeZHY3D/Mu/i+EdMikpPdVc2vb13ys+Kz+3s\ncxqSUN9trrOoDDRQGyHVtrvtb94jHDCSIXTK7jdaof81Qu1wQlTX+cW77KD0\n/D2o82Flu45miN3ErPkJbYU/49UdIwh9O5eMgI6tIONymLS7V0rEsyQQ1sET\n/uAL853rYGlZ8HS6qXll5E43tvgHGYVQqjbCdMXeENt+ikPNzJyA5Fwxm6gd\nGhbbbUU3NvmErsjBpfywPtNUXkx2h7bAKmeUF6biBDqNSyoh+1Y+LdjpQQd4\nC3BK+7TLJcldo6TSnwjJ7G7Y4qReYHygi1H/JjyL1ySKXcdch787MDYNdZbe\nxBTc/CIn3H4ya0RA5SnC2hK9ShEduhq1Gn6EySNz3UzHRW9zvQ82XxrS/HbU\n/qGNYQOlGRj5tkTzeOXJ4oXy+XlGUxms9kalg6HNwvV50rz7B7X3gWxVtcFl\nG0hUO3qGFC6zfWFc6t7IbP3X+hT7ySto46p/axt+Sp4veCzy6WPaAtd6INxz\nTI3TEfbjEROjB9Wi+i9MC1UmRELYWtDcmaDCvvm1S8tmjLEU1xxrd7iCF2Is\nNvKsJTAp+8Fhz9mWDLTCOOonFxnJgJHmhaTv7ofNMdO/LtPDShu7RtChxPmf\nq5n+\r\n=Aas7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHc4JlCfJvBESkeLp0g33E9hrDXdDHFj+VK1Eg6JtYAvAiBapfgPApwGDZCbhcbXGSh5VyuWxjqJLh0WLJqNfSUGaw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knp1j82w.0_1618863534963_0.029122307487675636"},"_hasShrinkwrap":false},"1.0.0-canary.knp3gee9.0":{"name":"plumier","version":"1.0.0-canary.knp3gee9.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knp3gee9.0+0052ec9","@plumier/generic-controller":"1.0.0-canary.knp3gee9.0+0052ec9","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0052ec9b00ddcc839457ff33b5aebd29ab0e9678","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knp3gee9.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-UFSaycONn+EqegGtATT9VVznm6jHiIDkOsq8rdRg19gTPK+a2yUC24fVc26WNUiCNCLvZVkt2fuPDgxc9K0kvA==","shasum":"5232a48dc96d80c5abf45b5a9016e4493827292b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knp3gee9.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgffJMCRA9TVsSAnZWagAAQ7YP+gMUsp3TrhZ7ap826nlp\nG+wMCMCIXuE3Vb5ylN6gHcdjyx8MXtbtiB07eh7XfpquSCePc8vo9IeUYsqF\n6+hgkWBq0jFnXyQGB5Nl/5dkuhgdmjbiQBN+ef7U7RgRw+r4F8HBilANnotc\nK51zXoOlge79NixynZ55nDpU3+PTR5RjtOYe/g0Tiqoo5CCZMwl3TagS87pb\nCny0GlAeBenzn5DLSxeiJnetUOSHab76NrGuNEldNJypCeD5b22LU0e0gVYN\nuBqDVYe5S3eKP1V0E1CNrwcPE0hV0Ha44E5kt6Qxv3CbOaQZ4BmD77NeykrQ\nXd7TliXJwG6nYo6GHDuijJSBc3gjmiC7CTIPyp9fbbE9qedisLLgRoI3o8eP\nEcRU1994ipvv7mpwc0+UtTqAim0PYQaG2X9AT+nF/kn0hamAm/l7k0fv8v/h\n/Eo5d8xVsEmTBduybZ7558Lk8pSS793y2bCP1JhoXHbEvGvmwv0YS3yuytoU\n0CK4dg8hrLIiHYhpeqzPhmSkf28MpOM1gmE+QvHPNDIUUJp0/swLUx4lDLJS\n/2zj8uiqxUUpHY5QuV8QkpjQDE2MjbSo1ECUCk+pL0XgjKDm24KiGQIf3u+B\ns2nNu0FGAj3X1x2yiKeZTHRbGKs4QxApay55UkIFpHAiYquHEyAGSAB2q8A0\npYRe\r\n=X1Cn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbQ3Tfv2/Cp9oBklJxfQeyQ8RAWCGQHjoQ8xdotfGSlAIgV/8YXP+saT/qbl+dLGBn+oMrQENIKwUCaVPOn9sPFgY="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knp3gee9.0_1618866763603_0.4175342401377269"},"_hasShrinkwrap":false},"1.0.0-canary.knp3sfsk.0":{"name":"plumier","version":"1.0.0-canary.knp3sfsk.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knp3sfsk.0+64a5499","@plumier/generic-controller":"1.0.0-canary.knp3sfsk.0+64a5499","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"64a5499abc4631ddd0451b4124e6ccdb5deeadb7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knp3sfsk.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-eRfLrRYEO4BWJ69ogHF/4L3YWaOv+ZVa41ECvU4SI+RideZSPDE9ZASEl1X9bK+fs3Tx90lOFU1X8Cd9QYikkg==","shasum":"8764f24e020844877474526468803987791f5a86","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knp3sfsk.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgffR8CRA9TVsSAnZWagAACtEP/0QDWMg9yGsERMf+xSat\noSPre/X+tp/ATN08Z2yh3L+SsElPMf+eBj8bSatWCGgl1OBbV80MxIiwue31\nXcV3zKSlZrw5guRRAwpmtG+Phwwjl3LXyT846Gc6MCcg/A85xrBb/8C7CROY\nPmrravkeQClIEI/0afzibkZPFCnvEXS+ra4gFUX4fEIbMf5nqLF9T4//jpL/\n2lP5GOmBvbrNSrY78oIQZy0c5Ek9s8PfzGkqVkr/tm9Y+/eZD08olijvEr9l\nnl1apJsYOmILzkjtQu0OeywtAkpoOJtZG6qDsJwrm09I+cVt9HnkJZvtF5Vi\nneYTEtxZfUi1z/WX9RemG8bdN1Aw7wadOZUl9VS2ndU8eMFsvlEsnV1N7QJT\n2rGi5ZQtojmsm9DzlXSUlTwuD8bSp/fnzq9+uLyMP7UqcqST0o7v59P7tld4\no/qdr4pTCHzMfFb2Mbq4l39BFP6CGP5vWquPQnVnPuwqlF5u/EYRnwm8gHcl\nZ4K8YCHCGWkew7VgKZ6cmyR6olvMdQLWW0jeVpkIHkDtN8dPw1u8FuS7Ct6M\nWW4yOWzdsnzfV41kSRPCn4IyYvaxjK+XFpxOlyIrB4PdQ813hFZ30LPc24WW\ntOx1xtgyP6iSMFDTB0M5NurBhko+KpevXLk0wQ1HP3Gvgp4Gt1lzVTRbMyuj\nQYJ/\r\n=FHpM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCGI14P8ALwVj/XW4evua6G7AOCocFkg30WXUDol+hjcQIhAIvj3i235oC2HFgCrJVIqIsfUDD5N3Z1aK89YcE5n/fw"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knp3sfsk.0_1618867323743_0.7862489526776353"},"_hasShrinkwrap":false},"1.0.0-canary.knp41y12.0":{"name":"plumier","version":"1.0.0-canary.knp41y12.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knp41y12.0+1d9ffad","@plumier/generic-controller":"1.0.0-canary.knp41y12.0+1d9ffad","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1d9ffadde4eb52140a9ebc7f807c6ef290452943","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knp41y12.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-Wy+DAxIRAt6mtbaRlvcxlXXmheGGnQtbxg5uHAW+BEOSb9v/U9C8R7oUDk9VqHq1Hgmk1heYHfDcjd9Cm32lJw==","shasum":"fc5ce4a8c51e97710717c8e9a3547dea1db039b0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knp41y12.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgffY6CRA9TVsSAnZWagAAgxIP/iSg+X/UPeNiwIl7y+Je\nNycMzkl5wHTCllEnbI3E/v+5JrllDHFBLYxWcogykiV+AT8NaH3M7wsj+Ts+\ncLotegJaqsKAgFqw55iZZLTpID+Wm4K8c9L2imxZ/SfGed9NuhGhFjbHC/93\n517lXKQa2sEeYbb9aDBZ6BIQLWDfMjaxVVeWA8FlCNAHjO1QE/EZ3oR7phEa\nhhYy3wu5zWwyGiOVY0b2Twi5mE+uNnaRJIFZoDpTdVVFwJaoSp6prXgOQp1q\nhUjT5R8Pst3r7MpVqmh+Esf2CZ+ev5QCGUet9lSc1Whf/TLLtdtrxPByxI4L\ntijYhTIY860jvt9JGFcTeZtWCkGn21Pkhj+X46aGwAw3TrYDo5oWdWHssbkS\nwQ+MQEI1sGwbHzmCtJC1iWwUZcjZr/xaiE72/4xe9Q1hE9zO6gY+yjxI/pws\nlHGJMXRvRaaFej+42Iubd5+79+uF7Y67yfkPWZaCnM64/qi4pVLQJ0VJ02Er\nzDsTXloyUHcDqeJKGGxQYhz6gj7XccCOycUA09QqD0sg7mKHWgLCN8WK/bRd\n0J+ilRD4kvFm+I06d1iarUsDlyy9vMydvKjXIx7MceK8eHqoz9mR3cSU5E+d\nRC17UZtAIA05Wt+ak34hxdVKEEtyP3LJjMMDJ5QwGpUxh28Mzdl5MUovYKmQ\ndPK7\r\n=DG75\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCCzCuKjeDIXJ0IaQA5fHSDQ27zejP4wfU+XMAOqnSYbgIhAPRWOHuaOBqv4D4KeSLAg0XJO7Pv6S4HN2ggPUX8BfPE"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knp41y12.0_1618867770101_0.755897611532631"},"_hasShrinkwrap":false},"1.0.0-canary.knp4em2q.0":{"name":"plumier","version":"1.0.0-canary.knp4em2q.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knp4em2q.0+85724be","@plumier/generic-controller":"1.0.0-canary.knp4em2q.0+85724be","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"85724be192820847a38e08023efc7176fe4670e6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knp4em2q.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-i//MaIXrVWSaHkOL/GjwIOARWoRcpPuQH02jeXZOdEMgvFs2vsdNYP9ln9764ATP/RW/KoF7icvviR+T0BX3KQ==","shasum":"966a954eca5939d3eb00c2d105b1586284df25db","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knp4em2q.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgffiJCRA9TVsSAnZWagAAR6UP+wZECHdk2K2qsJKiQQSK\n6tMbjWfLqENoi3b45IWMz/e94K5AJgUooZcMYQtXm4NsoAJPDc8R5pwy1a6/\nWX29q49B4ylMxahLxog0IXrvIUf1QOa1HfK4A81wKbALrcpjkGGZJ/k2ANWy\nWHFng3WMedzKVdkaD3LWHBCt0qEu3HFhoVGyQMn3HsO9Od59Yl1tCScNdwsf\nZr+9OjpoDL3OLKuI3TiF7cYH0crH0rtmzEliRoHIEY79SM1Gw4JMSrHkWv00\nHOssb4TRTwIM7l2FmmUFgFgHBSZJZKUr6s5R3+T7jq3AN4Ip/iyQgZBgqR6R\n7fiTSKsgYZ0cGHoJdmv+tNLJYWZqjB4bZ88hxJpoGd7xsT5a7tXCCS7WVIFW\nQXCnSF/k3b4KIGU2ZptwUxsq/EqHLpRYbXDSXyMtY54mhS79SnDUW3Ijqi7L\nsTUzGkUv4Y16EScyvJifKaEbUH13ED0XcyQATm9H8mZbdIR6HBeTj8BJhzPA\n2Fu6Zw3IBOQgpSw+zbAodK36LRV50g2wQ47qFOlUYGFC+lXEIokm20YnQXpq\nfu+NP7UKPaLroPwLIPAMiBEAmPLk+j01PBe3Zwb0yAkhLT9Es2ECF/xsGBho\nnjy/DHdohczKHGp8GM1f8L1UFbnkV3Fq1L56OADLJG+Wta+D/E0mN6jCqHSO\nZ7Ew\r\n=vdCw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBxBUBTvHDLv0c3DxmrTgQi+StnqXBlNAuogMi3pEjbCAiAIs7MGmu6plEMtqOSyzVsbWWvMvyMd7AyKBOVXeeKACA=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knp4em2q.0_1618868361494_0.8433559230184573"},"_hasShrinkwrap":false},"1.0.0-canary.knp4tvtv.0":{"name":"plumier","version":"1.0.0-canary.knp4tvtv.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knp4tvtv.0+039b103","@plumier/generic-controller":"1.0.0-canary.knp4tvtv.0+039b103","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"039b103d50ecbfc309987a7792c30815a79a5a07","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knp4tvtv.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-35PfZXRLGUbGj3NyTxSdFJZXr3oYh9vO2N3bEwcX/WdHDE8MSf8q8N+oofrD5+zdoyuzJWlejQcKsAxIHQZyEQ==","shasum":"70e8aab38739e0f55b4d9d128b2fc3e6d2679ec3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knp4tvtv.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfftTCRA9TVsSAnZWagAAb+oP/jRuhPmZT1if0yAOqQYx\nrHsfU+Q8eAyjLAPqR93PUffxc9EUcEEcFl5FWd73ErMJIz2KuyfSjFhqk1Nf\nz1RHUrtc2qSr/I2k6DbdzuZJLOaiuY2E+6Zsl1oIXsxSmTDxALUlaWJNGSGQ\nRXCOWs0jrbM4rOuHyd7uM9PV+xx5xyzdVh48moaAOobGNvvBejrDgWfMbtz6\n788S8zYS2lpGK57hI8gQl7jfJJaVI5FQopClRM2QGuLCri57TlBZ8ZyLa5zj\n5AoYMmR9tk+Vv34SfvcBLulb2ZHe5Qz4pNqiqzCjJ31t6FFmGUtouEoeAi5Q\nfmdcvEHs762z0XWy6XMda+kShDGA7/aVmOdLhDsO4tvQkpsF8P6GREoH/hwp\ngHgq/A7OqON27Zl9oxyg9yEDlpbPEYf8wlw/zAFxSKQVit8iDHB0E8URGB5a\nazDP1wzhbcu9I5i5T8A4vxXjjK3ALMRBdPmgIPNitXSBH9P4Uo1LDE0na2D3\nnXuJQZnBcywLE0xqCcbX7pUWSG4azQM/MPcRDQoqU5AFqgmFfs7lRc+SgEem\nf9dtEmGgDYFpDccUMfhdrQU7A5MdAjX/bto+4ONsiB/mGVTbnhn9WdIUTOyB\nkowkLyaOEqR0OpPuKgFXfwG245O+g034DG0FdRa0xXV3NGL07ZJGTphHMz1b\noBUQ\r\n=Vx5u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAQU/8Kpps0YTXMk0GF8ZgtUvoXg6zDFvjV1TUbrSQIfAiEAhVl4PeROYxEkYPfHy8fDyEeLbIYTIyK/7PAwj8xBOp8="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knp4tvtv.0_1618869075023_0.8545707450408517"},"_hasShrinkwrap":false},"1.0.0-canary.knpqbnp3.0":{"name":"plumier","version":"1.0.0-canary.knpqbnp3.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knpqbnp3.0+ac9f583","@plumier/generic-controller":"1.0.0-canary.knpqbnp3.0+ac9f583","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ac9f583239c852cc4e0014df27ab9670c904a069","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knpqbnp3.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-auG2Ws8qWgTHl/uGqaXGr2/g7cyAjyLVHZ1f79r7XhCsRbZe5NleD3JvTg2usaqXdyP6V3T4Brp3ZqjwHI5YFw==","shasum":"51f6c18a1443c047d645b4d14363c41c3f82fc28","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knpqbnp3.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfohWCRA9TVsSAnZWagAAFa8P/iW+WVyV43XXPBs60bN5\n6iGVtTLeZU04/ZDs7lcLuz/y3QIWAm5/5nR20mW038vsYCWZV8D8Oiyh6vm1\nlvElzPR5+EDGScW9ItYKe40km8B7MWtv2P8E9GKnxYzjjB6hPULls1K7rWFU\nKMrflOOgRWcBG1RgWIH62oUG2BezGAb79Hd2RQ05flONfrOqocYfGlG6BrB6\no2kZjNU9bSICZYfCf9GiPt+PoW7tPVkExkCavb9JLbQns4uxkGLguMkKQkyW\ndVDXkg2vAx2D7i+69wwb54m3yzZobPE4nFDpEevPPDJmrT75AJW8RoE7VnFt\nPL25j1agD7QM9VeJhkoCdX3sJ9nqg3RlzQNZdXWsIrBR+Cr/fzswocGnvDDr\niAlsStLbI/x3oaFCq86cImZYI3UXpV9gbvPXAFMTse4jBehbocSO1ITYpE05\nEMAN++i2xvNjLNocgVjzAcQ7hSqMNKLPDNM+Vec6IVwaR06CnsJSELdEn2l7\n6RXxI8lx81rzPd6cBjJlpDCzvdsxGwn2BgSILHOC7zwe9imRSX0reMThAhUz\nxkULQkBHMp7XDr4KkHuXLJqXoPC+L+oTnha8WSu2xagvC2xAZ/R30ydvfzF+\n3ihq3Dvd7TpbXt8pG+kt9ClOubE7Y4YaXrY/Rx+bBHIVNhUvq6LvDOQxKnuS\naDWM\r\n=InRR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmN0ZVjvV5fBuGNs+k+h0ZxtUSRHSOi4YxDgn1lgf4HQIhANWT3By5NG0d5vulwi4JJzD1SRLlz31ZbUnegCAa5igu"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knpqbnp3.0_1618905173707_0.5850683254223283"},"_hasShrinkwrap":false},"1.0.0-canary.knpqlai2.0":{"name":"plumier","version":"1.0.0-canary.knpqlai2.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knpqlai2.0+5065337","@plumier/generic-controller":"1.0.0-canary.knpqlai2.0+5065337","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"50653376df58a605aa9480336a53baa5290788bf","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knpqlai2.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-BORxid+88oVcuNhY3OTXINQv95nbhgIK0qS+ls+rYJmBVoELMgjBZTgSwyNX/U1XQw+zdW09MxwEk1xmXKq8mw==","shasum":"078deb72735d816ae81585a639ebd98460138e83","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knpqlai2.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfooaCRA9TVsSAnZWagAAK6kQAIFXzcWA1QSBuc/7z95p\nmVuSel+Hoggpe9vVJKjEuRrXO90le4OCADGfK6gAx5Jr5tS0GAPxDb2A2mGN\n1+zMgKo7rgjQgGnZesLf5jhWpfbb+q4nWYpS2ULe85oPHz793uJiDMPuDCpb\nepC01/XV1u65SPkYvZcpX6njXMy3aONSO6A346Rb5jUcHZumtswTxcHisl5q\n11LM8PKJ2+yLXWcUmWYKtce8HHs0rU2JJEZ07wUnd1rxN6bDnb+CCst1nHJt\nWTsZS5knUmmJbhLxhAuvyEns/IMtM6WhIfl8IMUBzffOY14Ia72RR7EkrP/J\noFILP3xWSW13bdXDGSiRIfMflGtGzd/lYBelYZOrbTFy+3qOBC4oArDoJ7Ec\nEoQQGXMpLEN6LeAVtgZNyCU1eqEOTfzQqA2DUgXajB2C9yKyX2WMZbTpIA9z\n2rV9zMyKO6HMQXYpEFK46Ztkl7vsakTkzMTkX0cum1uNsU08cdeOGJpn6Qb7\npfZ/C5irc4uwLbatA6euqJJWsAfzb2aupGyjpNcpAMvG0hoPK56SnigMYxLT\nI4z5C+BL/3v8xNhEJFUHQ2TPbLSOSnaZ/4mP6uFTxdYh3WkITfE0RnkxhFAS\nT97nLJZPQwfoO3YxNOm/KsD+Dq87dO7Gx0A7jYWssdEQs+LOS8Lr9kqIBOov\nb/1j\r\n=ylDo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDeoWPcIjgfVUWaTiCVHDmunH/ih29yb17beGOq9W6JaAIhAIdbxxCYp3QuP0jrWNVR19tqMcuiZD0wq+6pdcvT1qwC"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knpqlai2.0_1618905626492_0.20789375048451175"},"_hasShrinkwrap":false},"1.0.0-canary.knpqugsy.0":{"name":"plumier","version":"1.0.0-canary.knpqugsy.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knpqugsy.0+a9f5839","@plumier/generic-controller":"1.0.0-canary.knpqugsy.0+a9f5839","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a9f58390fabd6f30d235e183a92461a6865e778a","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knpqugsy.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-4DsSZBKc+qd7+4Mrh2JguNC3miOTC8IYl97vLsTVSZESIRhTA6vP/S+mqCUuTB8s08dTTli22hncNmophMGIUg==","shasum":"3357b6f517cb82addb511d92dc7bb2c56f5f8955","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knpqugsy.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfovICRA9TVsSAnZWagAA7jcQAIjPUOGbBH6o1qEcCrQV\n5yYGSUBWAHHs5lsiC285OP0yrv5jRP0mIUAa5OLbc83BmgP1Eu0bLDJlm1rv\nGD2MlYFx8Ys8DoX/3NQINJsBbvakDg3agHIvGPnf9hE8sNJScHl64mvJ5Uze\nuPN0D1QiSsA6oaRxUwMxuTWd03TaTKVNmoPpWEdhlpW4PZGCmnwa1oBTUhxP\nVuYWF3vY4oMUeVpgVLM+6qtIuL5O41LuWlhcOCGkbRfeiMCr0KtDa101NTna\nMj3NwpYXPu+Loy6Qrl1VfABOIQIxEV7bnPQ9/QeUEhaQDqbzM+eGSjkTdyfu\nkDbevc79Qt6aGGQE1P4ELtC7sHyoLNJEGOaTScLEoFAMKTWQbt0IF1ZI+A0t\nxcHSTIruRdELtTVkqvT685pq6JPVDoV0zfKdnTmvl9g4zfPCbcKo01CsGnF5\nnlzgTv6QxGcH75xSZH3BRtqi28+0A1sypJhSLk1ULfnn7V0mxJSqwTPcspSu\ngVraSIVaNvEd0JG9F5lHw1En9v5enjoByZZTBpk5xdSvJKIunBF8mDv6NL9E\nCPkSe/sWdNeQqJBvyRufZjJToxhC0Oh/n38HbhCpVbpgUPx5RM6Z6S7P4e9N\nNDwh+F4SKq6tmPwGZ2GTBRPMriCPGbBs4korWEZBNfFWv8be8CSN1cIQ0pKa\nrAwq\r\n=E0fx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEngUh2a3NZien6RzZZOyFRlwDJrg/2KXE398Ijl3wFPAiEAkOGAz8p3vgFkk/dcd/BTUiRa8Zy7NXnaKpQx3wK4kQQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knpqugsy.0_1618906056398_0.8923302370686075"},"_hasShrinkwrap":false},"1.0.0-canary.knpr3yrc.0":{"name":"plumier","version":"1.0.0-canary.knpr3yrc.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knpr3yrc.0+c84363f","@plumier/generic-controller":"1.0.0-canary.knpr3yrc.0+c84363f","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c84363f88893199e57719e0a098205970d047430","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knpr3yrc.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-b001F0hhaaPhn7miESwiWWNfYbVJVJkwvq8GY+GAtqq9rRLeaPoP29niL/w7hbUvA9hmXhKbd7x4+4SCT8yjng==","shasum":"9c8db9fb7cbe56f2512cc29e38e9bbab405bd560","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knpr3yrc.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfo2BCRA9TVsSAnZWagAAtxkP/1gBnXdLIRk4oHl5GbON\nNLkjxmwJc70IYwi4EevomEijb8jYUJZapkDhO9LoO/5ktGKkq3mKIu7MZwMH\nqLI8QtdkYlXD7Cyp0h/qsm1STDSTwGuYAxZY/YihU16EU20Gh5hA56G4edqy\nsqco1fBOLj1sK9FG7sUU87w0gn8WejrTUr05NCntMFI2KipUVZmA1BkjjXUu\ni/OI7e4BdzgNr+8ufC864cltv9B0ODtGarT8ziTpXiwCFY2oTHVyAN714LNS\ni81eHuFSM3HObbDZOEgGeQY6ElCRW3romBbAYfmQ2DGZcGyd07ICg+NodPix\nfHFjsMx53hjY4IdIJ2G+Ma3xULDrvyjCj210X95+7NfvjC6EYIJkvgZqXYYo\np5pUdlG5+n5SPIQzBWU0NaMPCg7zeDGD+hXo1GCqRqCS82MX6zndON6gfvpM\nNNBojjsKlCyIMprU01BFN+VxBs03QuYa3bd5+QtUM3dYvemBOVRACh7M3wcU\nvCN2bet4F2sl+QQiUf7JxMOJEJjA9KJKV6bLvcQc6H/6O5+ThGRJmQVVD5yn\nDGqidzu0pvLkoQdshPTma5/D5m7T854JCbexbfK2AkW8W4hOpQUS578J7p1y\no4/eFhNO7uyR4ty33uF9kWtotzL1mdYa4XeiLbuY+s3xk9FpjSO3Phslwy9T\n7TQ6\r\n=RfgL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFRAZmMbifnmvqDQMeuD+HU+rgAti2mx91LVWfBhy4mgAiEAnKsxa8WMM4uJBN9PNR9X5nroq/SbvN75pl+wjxuIebA="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knpr3yrc.0_1618906496598_0.423287602810583"},"_hasShrinkwrap":false},"1.0.0-canary.knpredha.0":{"name":"plumier","version":"1.0.0-canary.knpredha.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knpredha.0+908701b","@plumier/generic-controller":"1.0.0-canary.knpredha.0+908701b","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"908701b5db746e0dd9670be73378c236eb8cb675","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knpredha.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-GLNmrII+f4Om/HXuwqStNnBhqlba6kbaarhyg95vCKK5hEQJ7By9uikOU6e/8uDs/AnkGNokRmtp1JlK6dqDSA==","shasum":"053f59ebb734ba39c10daaf4f77e2d442c3e374e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knpredha.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfo9vCRA9TVsSAnZWagAACpYP/0WDXLr50xYIDDmWkFvI\nnk/DaIECsNzSJX6xBi48dVmE0xE9m0Cz/2nWk905h7PtRTw8y+nvAPr+TJH3\nIJT0ILXGfTzZtDndYf//agV9qRmVJkSLsyBedYnoUhth29QrTf4RKovxjMKx\nV1J3lkIS8n5E0ydJeH8GnRPhqNKG0soP+H1T5DkfjGJeDQxyN3PpY3oCbqOf\nwqHnusOX0vZaHdh5/eIJY65N0cgY82fqJSDACWPvy/O9A2S6hEIwq1Rn8PwR\nQ52S7fcl/1YXBYMghLO3Te4fiTeXjFpDg1Ixc9Haj0S9yDmmuMxAH4U9Q9LF\n+l14TxZFxpylfug2jUHcOpzeTuHbA70yjNFRBQvsWI+vtjWxIS2+MJsurw85\nh5/8dt4MvuIWhVjYU749XLLqflnbi362bA7jskl3oh6fshr7fxgp0ZX16HNJ\nZ2fzdZn4ajypM5as9TjS3UjfPWH2EYNUxUizX3eUTQI+DG1WWIG61KCWCno5\nlpYVKbZHU+eKJSaWe/K7mZMPCZpS1uz+9hT51dRDOPjO9PzrxVxGoD/Pkmx4\nJL5/SVb1ZpavVfEHOuPPwpH0vUhXnWrbERV7mEpBKbJLaZphwtKxEdKsDIKA\nyqWiwVZZmzoHazzrJqkfvGt40GtuS8tTdzoOo1g0UAPhbvds5+/WPcRFEvfg\nYXeB\r\n=IbfG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH4tHSMp7IW/eRHuRsDj55AXZKrbD8rXGw379YKV4gsmAiEA5mlkOja4eg227GF1tNDF+dOyMKWJgbiTreW4PKl0Mi4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knpredha.0_1618906991165_0.27135043336127196"},"_hasShrinkwrap":false},"1.0.0-canary.knqst0tw.0":{"name":"plumier","version":"1.0.0-canary.knqst0tw.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knqst0tw.0+3341de3","@plumier/generic-controller":"1.0.0-canary.knqst0tw.0+3341de3","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"3341de3c870f931e93de9920aa085277e5a7cf31","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knqst0tw.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-p+ERYErVi9cGHIe6vfhGub7EdtAMxjURVjD3A2F83zAzQrH0edh8O7M1VhbbKliuQqLoS8ofVHiNlm8ZNv0WJQ==","shasum":"0dbaaf19e2b37427ae4409ef9d19b5fc1d09fadc","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knqst0tw.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgf4TWCRA9TVsSAnZWagAAzy4P/RwOBtf4UxJbIOGiEh4w\nJxl2vVRLgHRk2CoB8cot6VNzXQ7tFioYFQ550FyIEdPZcZShceXAAYpEbFNF\n9KFtbCB8PqIrknDmIpLIb7YSK/EJGqB0+5dehAVLF3E3YTAkcw/efECb05xG\n57nLNhqoUgA7VeGV/UfdwaQ7ZQ+Fp+5eDrwyOlDlcrZXyQao5DrR2Ek1py72\noPIQQ2/4OtuXWR7ft4Sf96luwXG0yvEO8r81L1TkpbvR2kBiMtX1Zh0NPhcT\nZs57lvhATnPWEGC/yNj5ljJj+AU8avo956M22CHXRho90g70GMrR5TgB535D\nZ8orcM1NUkBDZH05830aDp39bTRyl5SY7IlPx/gADjidl+cty6HI97n6/+LM\nqynxIf7LLN3h7Dl+lpDCQQdgHDp9HOAXOoQeVNmtPCpcHbs5I4AeuEEtPm5N\n/a3FgvIGNIdF0r80Fz2WQZaev/iuWqmcieimMZ+7u9WnfF9Soo28bGWW/CVG\n72q9xJwjJ7eMQLSLIH/vBp7lKOgwYy/y1EWF6KdTQk9K1q76ifv2s1qQFv3R\nf5NCR3IJddTPxWu8AMPC4PKyyDLej/oLcxmOdXoetnQqSam2sgnGQcJFWEMA\nf/64prrFFCnKmmZdX0998qB575nRxRd2CC92B2NLdBIsHnuo325kVG1v1clW\nTaBL\r\n=axWs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF+0Wr1M/JD475dmmIC6z8R6YSI9wpkne6VApXeWKXe2AiBxdTOtQa7alrxf2pUvjtITmelnKGzM91rq0ApI/HRK2A=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knqst0tw.0_1618969813856_0.9984475552255532"},"_hasShrinkwrap":false},"1.0.0-canary.knwbehg3.0":{"name":"plumier","version":"1.0.0-canary.knwbehg3.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knwbehg3.0+1a45943","@plumier/generic-controller":"1.0.0-canary.knwbehg3.0+1a45943","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1a45943baf65af12e98dafddeeffa7e206b9b4a1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knwbehg3.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-PiHLig3c7AmIoF95Z1yj8uEK0oHCfPn/6RatwPZEga1IytpTbZ4yuGZgZuoj2oDLpgxD41H4eZGrGKUsG0qcEw==","shasum":"996584b9b095fbd74362af350169ee2b217c824a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knwbehg3.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghJvPCRA9TVsSAnZWagAAOVcP/jG3cRh7fVqvt/n3AVHN\nYn0sVRBDWZQqk8kJNqw9azQlpz4O2Mwv0EWp8IAuw2S06GG6tlz8pubcF+Cd\nYLAIcmoWAxXCpggHtbn1xkE/X6rKpGnpwpWhlpQcc32tuT5HdnpZhh0B2zGW\nD3bCISJDdbafoeEU/Z9Kk50GWgQc3gQllC5aRRGbnz0DlbPPjEsB3xMAY9BB\ngg/AyyigyftoYrTal0uR1IgR93fJ04+ia/4LN5K6ywSt+7vQXD3YNLNPJjhw\n6/z/3cl3gBmNv1IIq366LkdQzDKx92wmi8km8nXtwv8p3ttrFEaZbi+GWv5e\n6MsitkuLxc3tZBrRtiAhe0yt5oB4gpYyXsAB8u1W8mP7WDHd44UeHVhJ1Smy\nOqeqf5Xiq6KZkvEydzoAc6UTuHECJtJuJ3VtaA2HXDkDLYsP7hBtRcR3vZTI\njM+MC3DaGyhKXT8ut4ar4VsBd53eyRLm7szp8OzvUDJnisS9bhSIH7zKblec\nA2AKm3sQCH4ne03xyEvZPdP3Bc1SM8KGQfClRcKHTsQLntVws6bSAQwZJWcY\n/fK4vGbiqeYf3q9Qi63VTAKMmgU7dLpnIcuEIZFFrZhohREViT5pJOAFkoOr\nk7fGS04CHBhnszTNBr6rzr1wB7kzWbBlYQVIdGbE8ZOkbXUZtbQ6Dzck+nHO\nDfC4\r\n=jl6a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFHBrHkC7wya6pJFfgkfzrukHqHSxQOuM4lVIQGzqiu7AiAt1kz6sZJ41bXzH+OTKQFtMHyrIkg//wXIRHAj7j25jw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knwbehg3.0_1619303374817_0.43474741784138926"},"_hasShrinkwrap":false},"1.0.0-canary.knwe89mn.0":{"name":"plumier","version":"1.0.0-canary.knwe89mn.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knwe89mn.0+e280a33","@plumier/generic-controller":"1.0.0-canary.knwe89mn.0+e280a33","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e280a333f4ee737f45c200845dc4e73f8e89975c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knwe89mn.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-du8wMPiVxCAf3vaQhxFePPTakKgmq4kzkPjbR2s9CqMz7a1Eq2tTHqUVhv5FmDEKMNG2i3lRR/BnZZlVa5/XWg==","shasum":"a5cbfa2ff5368883673b27038721663a7d11bc49","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knwe89mn.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghK5ZCRA9TVsSAnZWagAAEuEP/1u9R9106pVokw/SH6T0\nc6bY6oB/bslwi4/zj5TZ5g9cf8o2PCYWI92z7kW2kLplkuj+MCy7UNyCv2fK\nPJZi8wnGqMpUW7vOeMuujt9AZMJywttkKbjfWZSzM2xyUdmwzZo9zOR5mR+3\njLzfJZhWMtZGdxgJC2rtGuwj3rwmnlRULhf1Ow04BAryEY9DK0+GZwRKJ7jp\nH0hMT4T4+YVMdLqlVQQC6/LjpvDfejCuXXLhT7EAUam5ZN/ikNZb4a7hPTmg\nwxc1gsk3Ffcj+982ELNm4EKeuJ3Qo6y5gLzFpiOyQUaABW5aFvC/idCkJreE\nEQs0jwkzhpnkmCSSKwywn6eNMvfJ3kQsLadYCnOvlzvw+mAFtma21nYrW6SH\nzFTV2pMhbjLrcbYMQGJU7yKYj0ZUZpZwjGyWc+5c/Y9HT4lpoTQ7+1gkPRD5\nKXdAn3fr32Kk1NkDy8yIoymmU0dGsK8Rr8naCcHCYcYE8lA4Er6oFFMH6SPu\nQ4eX0O8O+EYdFhtQn3EHNR921hPyqR+6RN7meZhYNrgGuAZFVynQIYwZzeRW\nKn3yNBP8AdC9zYYdA6Jyxzk4oConEk4+0+qVugkf9fG2JGDeMrB6PsveQRQ8\nOBGdjUA1p45LVLnnbDO+41aG/c3TpdL6FU9TaK6DR87FzItuha+trI71sO5a\ncf7l\r\n=ql9k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBwnQC4PEa7EntACWOHWIpHh6UWz86kSEVaXWzBsi6SQIhANGhVACvgEikutAK9y+hcAq+58xRogpnA0qR4aQcHMO6"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knwe89mn.0_1619308120753_0.7723938203030372"},"_hasShrinkwrap":false},"1.0.0-canary.knwelcuw.0":{"name":"plumier","version":"1.0.0-canary.knwelcuw.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knwelcuw.0+2e9dfb5","@plumier/generic-controller":"1.0.0-canary.knwelcuw.0+2e9dfb5","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2e9dfb535aef20be5027aa8c61500b672a12d8b1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knwelcuw.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-6AI3f3gDtWPChKeUkoDdPpfYldNgSvGn2YwSr0z2MHkh0Jyz7S0oi7+WxaL2cl+OOqsEmu67T3g7nK8eED4qvw==","shasum":"33fdbfe03aac48970a808893fc5deec150012b74","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knwelcuw.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghLC7CRA9TVsSAnZWagAAKasP/A/VZHxmlWSgbQYtyrQx\nDPBHA+5kUogatV7EBwoDA2fRK1nwv+ZXKtCRQ+PtE88ZukyxA6MNJS/Y1+qo\nkC58OZ2wbdq0xsxGLrJsRTfKGsLNqC2LwDqN7wq0GRyGbFfI4D8DpCmaQ2k6\n2K0MrvhUkFRTffv5WuqVrSwATsSCRpJjQJkRViXNE4RtnEWrxzlmE2eytjo7\n5++lozp9snVYSXW/Qs6srlhNLCkF9fT/lIXT4fVgyVfKeSOvK3ouajhymWVq\n3pPikPYVNLY8lMlZ6hJLIV8vEU3qqhFwJx8jSHQ/Hrhy1jsterFANUkrJ1Ie\n42zjyqvZwMpPN+DoSjmsa4/GOf6tdznLzQtw0QiOftKfgLQOnPK1G4RggQ4L\n0SFS8jkndU5gikV7nXBYGP5kz1dx/0KN+tIng0getw18AAlm02q8A1Nm1soP\nnBpyZp4NtNVP8Za13eQkijTWQ1xY6c8unD4qj/eJACgAvUldVKhTLVsSg9G6\nqenruQjDg2IfokQxt8VypJSLoHpkPjXfqfQjxXIP27N0sxIen0hP/cQI8s06\nfK4JEoBalNW/nvnP8AvKLECJMT+LskvyVwX6CE/gv0n0C2kHXVGWo/HfEzdr\noENsnjscNFqTSBy9jAWIWofGD1cs98NXadg13X3KXE0IQC4N1YNTu2W67rIo\nyJmN\r\n=BtMw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCz0oaCUFAf2TdwQn5+B1CSTrlkJv2amGR2A1Wz/dcpAwIgS8D+xM7+4kxWezh8RHN1TXMvjxYhAoQyk+TckSpxUbE="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knwelcuw.0_1619308730909_0.7035492092169449"},"_hasShrinkwrap":false},"1.0.0-rc.12":{"name":"plumier","version":"1.0.0-rc.12","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-rc.12","@plumier/generic-controller":"1.0.0-rc.12","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"37e1434132237591c063adcda824bc05c3f3d1ef","_id":"plumier@1.0.0-rc.12","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (darwin)","dist":{"integrity":"sha512-5KNyvJUCTg77qeOvoVnYsCNzr2h0W2UvbHBbIeN4vvSc7lXM80Sglu79IO/1rfL01KKevL3kqeXwchvQTbx3sA==","shasum":"24ef3691502a4689f238f3674e7db9c2d13de426","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-rc.12.tgz","fileCount":13,"unpackedSize":27856,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghLxACRA9TVsSAnZWagAAVbUP/02mfg1Gm/mqPezTgjvy\n2mudBmBq7gsPO4AoA31Pzki9kf5Q0NUkDmqqXyMhkSZP8eiSACzH2AiznSq5\n1qEKZ75OUGyjZYqGoqIpim9LSTD1QPLZ24luF6tXrt9SlEJOP3INh+5TdhzW\nBLNBADDWOur6/TaDr49C29lO9EFYz8mnEKObyPUEUG97GJyFsf1zRoIKf8qY\npUF32Ru6wx0u88fdTANjpxVHL/NPxh3QRfXB1JG9wKnGXBgyknJDGHX7AHRX\n2wjSL+WvS9lQzMHQaIIYkBeV1vSfM4LOtfsNxY3JdYcA4ZYqrN8mK1AAfb8v\n/LnoGm3HxYeyFxQeXiTMjYoOGovLKLQmvVA6hAggtw90G1ec2SLm/wsQFphz\naU2Xzr+t6LAFZBaiXsX08awWRY1SA8ko3vHCgCimjI4iy7xi/HRcUdFRzmpP\n29u1sdQNAhR4rm4JEwve0TC65rWS7dPicxbDplXpWh1gwV42hERsRbCjaZyt\nQPO3XdPVkUEU9DnneZ509XdlJucHF3mDJkR713MxCpSn4JjCOhSVu58mWS95\nXFlg6ox6KA44QTzxmK4DSPsd7y4BE5X+YWZUAH3rqslG47JxLfn+/yTSaj9C\n8ykBexLU+sjyicIexaGcusr5PFaumRJEF/8EIPTt2RfFbNH/4+qPJOiuCTCO\nvgsh\r\n=3v4w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEv5JycHWr0MG4RIU8lwMvFQbAUuZfOuZ/zwybjUFtlMAiAluriBAnO9cx3RLCP6Pqpn2tuoS/pEnKgTzh1ZubP+og=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-rc.12_1619311680280_0.3981304216024508"},"_hasShrinkwrap":false},"1.0.0-canary.knwgj07p.0":{"name":"plumier","version":"1.0.0-canary.knwgj07p.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knwgj07p.0+37e1434","@plumier/generic-controller":"1.0.0-canary.knwgj07p.0+37e1434","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"37e1434132237591c063adcda824bc05c3f3d1ef","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knwgj07p.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-jACdEwHPAxD3y947jcX/jzhzr+9OP64awiQur1m7zh5BDPeL0VyUXZdgJtdFqNV/I+/VYkrct4/yyuVDa/w2ow==","shasum":"27d250bbd0eb886442dfcf95e2657792ae53a4d1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knwgj07p.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghL1uCRA9TVsSAnZWagAAClwQAJ6Cq2mecnaxpiigZ7XL\nXiz6iv4sGJoDiX8uGusi1RSR9HmK25kNCc2PO5hmQq9/X2H2zvGZ3b8jjGKf\nSog6nxbqMIXIxWPenLRS/JTbjxjr/REp2biXDa5uXmE9AMVzLNkZ8LA2lT7Q\njzvLtyvHULiGNENHwVM6oJxx8tDJm2+3eR9xBOBWOhZfIRqwy0G6Dl7Pn2Zg\nAsPN2lsic9h5aRuJb7kI+4L6J+OpEM4bj9Zq/RrfGHa5nR2atLDXq7BmL69E\no2jZsRDddpqqTmqkEAmoxntDdt9Lradbj/i0QoTSByeidl+eyw0rOdC5tL5u\nu4sytI1zJjOX2C3GacTDa0NtOkewTVEQvvgXT5OatBwjKGY1hL4ARLxp0Anf\nXAwtTki1Oh+iAu1si8vok7QgYEAJlLrrEstKc83R2HU4WpCBnr3+Ixjv5pHy\nJg8q7xAS7t7aVbM/1VDfFcpz63c4l9LC0yn1UBUIKaDSxzAJnFb40pfiTez0\nO0jn8pw3Ud6crMOW1YCClCtebgNwcaR1+L1+ybZ+Am3qWRj+u+jlU0QymWyi\newUJ4L7emNLxn3dciNS8vFnsl5I6c2cTsa4ltC1YyF+UQTKl+hkAdK/5C/4I\n8yRF88ob/1OSLEGfcjPTXkOTTvGKOlA+hDNyOWMemeMDtJtLKOQtyKsPlMil\nA9iR\r\n=jWdE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEb3nhO5tWWc+d9BLdzsLCY4Gw/YeTQAHH7Vba/3tF1VAiEAgPYjoz34Qd9w5Rc0TZkhMHusCxCr6TeDlHQVaCR31Gg="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knwgj07p.0_1619311982344_0.5711917949968819"},"_hasShrinkwrap":false},"1.0.0-canary.knwigz60.0":{"name":"plumier","version":"1.0.0-canary.knwigz60.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knwigz60.0+b7cb2c1","@plumier/generic-controller":"1.0.0-canary.knwigz60.0+b7cb2c1","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b7cb2c12eb1298035917e8d83c27baf4b225f2a1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knwigz60.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-o6R+FGjvFYGIqT+fhkOBVz0dqMxnT4tjCKnqMJd+JP2A2d/RENXU02wHcnnFZKrm0CaLjSWuGmcVR8l0WGI9kA==","shasum":"9ffe0c149be65e7c06ea1dea6f906417092f4703","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knwigz60.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghMoxCRA9TVsSAnZWagAAVAIP/3VbgG0McRQ32Wmzz9Z7\ne79iEJ1kAG98zbogpm5YugmCqHzyXexXSIJT98pKyh7uZlIQ0B3gAx9peW1a\n+FDOAoGAJ53FP3xEc+AIGp0xs20hDxhRT9/gH00I6WVV11MOMaSD0KOrKfFC\npQ1UxZs3CNwMOaZ+p+Hws6jxv6l9Xc8Vvh4qJK6DVLw62VjB5mtVzuFxFoW2\nTOFZAa7sbRD3xFXZ+OxFUlp90nn0GscGY7k/B0m4jmrh/TlRCMhrwq6vfDDJ\nwzrBDXwmOvkKyI6ijNdq8c3DtUORuhNnSmFGvkEJRqnglKwEKl6f46SgdOxC\nMvHfgUDJmjdhkn7qiuLQA/cf6pjP6EtbkyqQ7nNLrWXFCwtqEVaxXCJGUow0\niMqFq9FAejMGU72xlKZQX0V/GOVLUKfQiKH4NSi70FXMjtyvFwL35wiUzrkF\nOyMMHeWyXPh3JtJKDw4ZEtAlExyWdFly4pISa2Eu3EHZTkRGEFkCaJg6p28R\nAuYSPyee1XnOwQvu/p3YePBtz19HZSAx9g8T7rDtYHa3zUbFiSsEAOsDHSAO\noJbel/I8XrHHGKubyxamVtHO1Sf9M2ldLIRWu/zPjyov2x0OhGGmJPoFiTHf\nrdkPyO50WcyfnpQ8hSU0ljiZ2RgivQqUnTnx4/twqPU92nmwb7nt0n5zZ+Fd\n3Mqt\r\n=yfr7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCphnmSuh6IQ1kRxmxYUZXUIykemYLj3N1oaqboWeZmnwIhAIqV6mrhnutbdA1l8yRZQ+3oI/GJRBmR7ifbVlAUy8Yg"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knwigz60.0_1619315248834_0.5715999106813594"},"_hasShrinkwrap":false},"1.0.0-canary.knxxq9sl.0":{"name":"plumier","version":"1.0.0-canary.knxxq9sl.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.knxxq9sl.0+9cdb11b","@plumier/generic-controller":"1.0.0-canary.knxxq9sl.0+9cdb11b","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"9cdb11b16e22dac8d49244a5823021d4510a33d1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.knxxq9sl.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-mjXw347cVjn85cvBFRrfabrHDecQ6eAwiHudg9aEmrlC25R+To9oSV0LweLL4Et6ui/ONdYKeClwqUPdPNSdNg==","shasum":"b7be7f1fc9a248285b2dcd3f34be7ed0eac27640","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.knxxq9sl.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghhqACRA9TVsSAnZWagAAyXMP/Ry7DyAoVPSEO6vrsCQJ\nSnpdqK8tICszWjm2WkkyII+CssgkklWDycWy68omvWAu0+L/pvJtDkWuZqCO\nwLo1LO1qgGguT6jEx0wRUi7hTkoKVBOP8AZwbuftoF5ZMH8d4TxxlBvysjRy\noX+qatgJETKYqJfWne/TdVsYZWKdvYqIBVQ6VkC83QgXLOLBRLCfKLIEBMia\nXz9mWLbcde/VQoAzqywPmDlSPgjQ0qIyaLaG7eVtUiuvgpNGW+YjyT7HHQv7\nEhaNSlzLpeX3ACumtnvn4s4Twgry3x3o/p2taXW64P2+XfCkeeR7/RdxVgNF\nCYpUPx7rw3otVFcl9tTWcgxfQOidKyjJ5H81WEoCvrOw6yKwrml3fFTMxqic\n82ID/je0UZfLYqNSKFhG1VrkYfCuY/FoG/LbgxesrKU0iGT2On/WGPkw0We7\nFFlRWbBs6QUp3XbF1YFdLsvUr55t6s+FuJ/V2VxP6ExAer5rq7CF4e4J1Gxx\npvhyLC7SOUQ2+YurPOic8aC7By3A/WbwcD/ctXDjypWnnM3l9lL/wDhjsZNU\nOY4N+QE0ldxOx+GpMdUCFyOf0CBrx1XdB9u4jY0f+4iUqTXTAbxEfmWLzQnL\nrANm1Ay0ir+AHhI3W8hYobsBAWAvuag7wK/4jt9WRxfSFHPmMgpvQ/fR4zH3\np0nL\r\n=mY9i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBcZjktVcCXKxUxzqhbbNh4YACJRKLLjKVtqMO7hhKlSAiEA8LM2LNqQKFnydzvRiDUfUrklCZayfnkYNndrUuRFvzk="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.knxxq9sl.0_1619401344255_0.9564349730236623"},"_hasShrinkwrap":false},"1.0.0-canary.kny3g8ud.0":{"name":"plumier","version":"1.0.0-canary.kny3g8ud.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.kny3g8ud.0+8eb2132","@plumier/generic-controller":"1.0.0-canary.kny3g8ud.0+8eb2132","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8eb213299f272b2b240e55bd2dc960cb95ea3b75","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.kny3g8ud.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-29gXE5dG9SmclOQMgDCyaEQgMS4/ya0fs4UTS9oVbrTgTHgiHI0rGixBRTLwEXL1MaWwMgLtwxWQP9JJwPFWiA==","shasum":"196d231d8f2c57d2c61d8194cf2cfc1436f726b8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.kny3g8ud.0.tgz","fileCount":13,"unpackedSize":27916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghkAGCRA9TVsSAnZWagAAU2gQAJC5ee1BeN5L58m+S7o+\nEXZ62FHXKKUjhXrbyZKbNKtFg0YmTE/XieWrGFS7D7fCMYqKvq8RmFUuC4/X\ntPKpYq5Q9GLqhGxHS/ODkFFV32/vSv4L6Oz3bRw11FllP1qrZmadhNz3AIY5\nmgEagZlLcVY0Wb/ZRLgTKDvmDMUB99+2/GNJQzpf/LBqczMj1YU9PCbLi1K0\nqLbehssk8HbaAxldeypF7t0kgxjgS28JQQiRKGx4ef3VajbzsqnvCMox7H9z\n3nlWm2x/Eirpm/DHEo1lEN1xSXLEtLBqMk62BRsiST5wFY2y5YOijU3xJ726\n3SLwfyyT098Ij0ff+6ALsJlgoLjTWMwK167a50ia5sWeHUJa5ywKDJxwmBA6\nIdZE0MHIbeD5+FHslVCPvdoB5uB/f5QQSfaWs1qD3dQriElC3EU3QOsFWWKI\nmUq3h+cOsi0aARRRRLrYoxnnzEhHowwqD2tYxPkMu7IF30astjWzNx6VRjtl\ndi8PGEPRJU+KxGoONU6w70CBcFZiuSZf0oS6Q9Up7e6YiDuFIgYl/VkNbF0Z\nH58KW2d66k3Tsjgz8SHAAdgPCEat3VdGHWbcmqfpF8I4VTqeECiGITnnGeiF\naBXbYoEw5DMPHDdSdCjWKVYhyBXLMAaF4HQ7vzmNzIHnR5+QbSwMnEFsPP6i\nDy9c\r\n=Gn51\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGsYJgy1W+BEyRUEPQTSFY+Vmc3csfxQ4rFL5JjQwoUDAiB9M6n2It5PQm1KVygNqXDDzK7QQf6yag8ewnObaEqgcw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.kny3g8ud.0_1619410949470_0.030183692670927798"},"_hasShrinkwrap":false},"1.0.0-canary.ko0m5xjm.0":{"name":"plumier","version":"1.0.0-canary.ko0m5xjm.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ko0m5xjm.0+68e8e10","@plumier/generic-controller":"1.0.0-canary.ko0m5xjm.0+68e8e10","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"68e8e10558287951986a4c68f9b0e3e5dba0beca","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ko0m5xjm.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-yxatATXRrSSVF7/kKTZHOR29D+Nz+TcCGLs6rXeCJIyot5kC4jFzoEkUAa/o2zmigR29GHy63BEk1bbWk1UJpQ==","shasum":"32fbe5d359cc246b6b67c956ce84373315cd2903","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ko0m5xjm.0.tgz","fileCount":13,"unpackedSize":28644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgiJMyCRA9TVsSAnZWagAApSIQAKIy8h8jI2bAonAVcAK/\nJRZojJREIxACfPV80MSNfM2HXxSWNQsm1KYJs3rGaHwpZnwJO8tW9RWQV+6P\nP0+fyrqBju/eZ1HkFmbl0G2ey89X9e0W0puW7ozSn89L51zuoHzi21rC2isY\nuvFa8BQBMo+KZ9Uq2kN3f9AbJAayFp+F/lCSNFETpiVuSTenloHNkd8hr6hv\nzy4SzRST3a+26V0uMTglFtkir2RNm/UFTd2IQwA33amBsA/7T0dJ/AjxLFAk\noNivikf+AMPkqNAAgJpBHA39KuoRlga9a5GgUPLdFlwxZqxoR8B74+eTUIGt\nGJYEYr6ll06bYqbHoKGq1MGjZS5igxhQrO29dxbFAM+6tb/dZTacEQOdMc98\nLcD6K6B3/0Ni3AI6/+BGmdoBih/cjuQERGGP72rNwVXvzgI85n8iXMihjiJ7\n1vzsmJxgQTM+WsedmDljKMbcm+YnYS7HRqMLs7AqUGyFyHJGwQLOd9o96loD\nbuM+BkjHDJTKcoyPKtwmzZxLIr0z1dSBPFl9Bqe7TMxkim6HgTuR2fFmWRcb\nGMQj/+rOTfIQBEhbVmURGVgbP0hJRmlVQQN8abcOZ3TS0xD+6a2Jmf/Ofqh9\njJaIopjjyv/f+WObSokVcRmJ0mkT1nXtZ5VIUXIszzT0swlUosXzanZA1akA\nqLrY\r\n=lBsR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDogGnTI+PmIMD5rVNTWWXiOEvQ99eAzv2BdFuTZr9RNAiEAgeO/GBFWalXGm7zjHltQtAczNLeep9NfYCODSpxvzfw="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ko0m5xjm.0_1619563313653_0.5207597195906035"},"_hasShrinkwrap":false},"1.0.0-canary.ko0njduj.0":{"name":"plumier","version":"1.0.0-canary.ko0njduj.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ko0njduj.0+86c1570","@plumier/generic-controller":"1.0.0-canary.ko0njduj.0+86c1570","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"86c1570d3580f51d64e888a9270c0a5fc0bb27bc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ko0njduj.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-ZCmgGaEtt32qW+HNOU2J1re9DFN6Tf4yDWMnxcGvAaymzzSi9oKQi71tEUUDYffsU5wcc1csiJFsCH6+IzHFMw==","shasum":"535cf82d18b18826f310d1b25205d9e3fec4938a","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ko0njduj.0.tgz","fileCount":13,"unpackedSize":28644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgiJxCCRA9TVsSAnZWagAAgY8P/RS8xpWQdkPnS3pKMEKK\nRrIQCbumRjnNYrltXu2jF36Fd0+eOi1Lb1p8f9LfK1CwCIljFkgcESmy7Vz0\n24/RNlrUy/aBZ3P5pyATFvOGKuEr2CrZPNVKeii8GpdA8bfzevvPm9KZJR9T\n8fKLpaSmYKzlMEXP7iPotZZgaPKfbxADCE8dxtAlLb/wUIscm5LDAednPonR\nWIE2p/th0jh3hjogFX5J6P1ggZeMe0ndY37AZrk9bU1YSx6m66PV0HFPF2A6\nYDQHbeALRkhYPk2mOn0JRRInM1XNhgE9vW2yMJbcXr88u9U7t7xJML7b0mwm\nuGh3uVDzfm86LbHxohnYyABo61X57pTOW9HugNDnzGuEX0WIAZwIdmhZ+I/3\nw9cy9tW+mqX4f8eV8jY5A7wgQI3wA0WVE9vcExvlhIfbOrJYO176isl1gR3e\nBpcy8EjLnIYBhIiIoXnSJ9r8kQ0IJsYaKlzkvxQHDUAfLwFEtmuLd59GtScb\nA8ImEzIMSh0KSa6Y7vZJP/9E0o873FaJLa6QfOLJlnWjFQQ3ULEUkynPoS9c\nI7lmTS9XGdUPCscpvrdgRErxFVKtE7Id4/OMTjGpC5YkWrR0wBxK+Vsd0tSw\n1rpFztDSrp6iOKFOuMaIQQpC4Onjop+GvZkRE8CJDX+0/FT77Z04wEPkKWtP\nUXqf\r\n=HY5K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFpFsZqsvXUbTDtOBfyY2yxGqwWJsWM2l8JqbTUltJndAiAskhlOISOx2KAaCYoxMOCkl5wQGoiNeK+Ym6vBd0HeuA=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ko0njduj.0_1619565633601_0.2599218719511829"},"_hasShrinkwrap":false},"1.0.0-canary.ko9izbbk.0":{"name":"plumier","version":"1.0.0-canary.ko9izbbk.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.0-canary.ko9izbbk.0+89cd9be","@plumier/generic-controller":"1.0.0-canary.ko9izbbk.0+89cd9be","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"89cd9be66a3f77e176b55d241ce47380d08dc5a2","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.0-canary.ko9izbbk.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-26TdnljPh/QAOoH4OLN/UmwpzDtpOFqljvSgXvUKNyS8wYB5cVjXtEd2BPr2gI0zAHjRX6cKuX72Zavmt3u4SA==","shasum":"8505bdd1da38be60c3719fef7998c59f2059df9f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0-canary.ko9izbbk.0.tgz","fileCount":13,"unpackedSize":28644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkMwYCRA9TVsSAnZWagAAYcQQAJvSdWK9nkRH9jmI9nDx\nkO/El02yN9+ZIqbGxuiMRbT2uvAhO7cGgIlIW0inimBanPOGzNjEmvSDcMAw\np0Y5aEOt69oJnMeIvq7ZcfFMZwWvtR+rOmUGL4wQV+HReL0ei+pxVMEkdSlN\nTVWHEDOWP1Lf2r1Msj7/TArA3UQVLKKeaQmPEmvDsVNatX3EmHaHCYvL9Bww\n+bj+aPCN62P3U7F7PCnZ5bWRk/TjpfDqVZy3hcxYr9Jl+ePpYYyXf5nhXgNL\nltBXJYMhdR9cjJeA75MWxvbQaPYzKd7j0QMVmqCh71hmioaC1CMQ/oM5MjoJ\n7GobEgR/ZtFFdksaHAe3tEp74kbYomb5j1mXkiX0Jd8VYFUefqi4M+/NhCVC\neWaqmfn2/4bFeHaButg+jih3YgHuQiwwdRAQ3ri7EC2XhiiC5N/QoIc/GWaj\nuLMRI6x1kyb4tdK9Wje8MXwB7ffowiOQY4XAzwco134o8jJyNJE57TObfCBt\nXfi2xSyJyc5HCr8Tbme6ri44w6bfIHbl5mQ5A+Kdtl1Go8cDq4Uc/rAACsDj\nqaTL+HkmONcR0aAKWfOkIEpxmuXJE+k6UM6uKj6Ovappg427gZZBxRLAepoT\n7RbaIYvkLQcPwsrCZNbciaGftBQ6ZxVTrdIIoGTLOBJkMaAbQ8MhyREZgpgP\n5cKL\r\n=ZYtN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDOgyKWTLBvIEkVI9GX0oFYgXtH8tbO2auQDCdLJ19B0QIhAPEnBqTwOULCntAkwoBvy095HR5sh8Hom29Pa0f0gIxw"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0-canary.ko9izbbk.0_1620102168232_0.6720084675318883"},"_hasShrinkwrap":false},"1.0.0":{"name":"plumier","version":"1.0.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"^1.0.0","@plumier/generic-controller":"^1.0.0","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7e8d4af7b6163780d960e5820e5dc469878e93bd","_id":"plumier@1.0.0","_nodeVersion":"12.21.0","_npmVersion":"lerna/4.0.0/node@v12.21.0+x64 (darwin)","dist":{"integrity":"sha512-3cuBqw7LLgKZycXPdwjMgkXilR4a+EzkZqEBdli6pa6MNUAncpCWyJwjDLgJgRIOi334l7gYqOGuq3rhpGrhjg==","shasum":"69ae4b1531351f341d1fd92b7b0811fe380e1a35","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.0.tgz","fileCount":13,"unpackedSize":28568,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkQxRCRA9TVsSAnZWagAA6oIQAKQL76PoxHz2lpLw8ak1\nQH/q8XTyDiSlHbgGId1IJRhWXXOdT3XbQX2vniZPj1IP0Do0gxGzfmVHbkii\nvkwTSJuj6xNWX67Az3sTaZRrWnep998Eui6SVYUtIb6qxofpWB99xt9VXo03\nbTpq2EGliYG4XmhAjC6iK7RFdDblTMq6cWVNvjG/RufdJOFgUJsQqXFLPQOv\na6mo2QV2Mi0TaH/5iz0TRY3xKZUxvd5RjYzcihP2aCr4fRXiDzF9mtseRY65\ntyCsYgeiL3Dr4qnYYSX0x3laC1pjt2TBzR2wRJz9B2Tg0oODfrp/Vt8GWEcl\nBoCTU6b4mhjZLIr7nzu/cngI3Mu7nDnDXaXJkSEWJuwyc0Hf4S0526ocofUQ\ndQtTq0j6I2uHLJ6gUm9kLhYCXHDZETrmp4frClBewOYCPOmaqu77qPplByAk\nbvrXRSsrUIOHMKEO+ltvDSq0v8lTVuSS/K2IcC9Q/eFvViutOILE1vmClamp\nerPg2f+0LzV33sgM51i7h5ZHUU9XNnq0wtdh2twmjsnEdQxW/h3gu8Zb61RA\nzjQQRhiC7Tw8G6Xzxcq4i7sYMF2OO6o5DTohUo2UEiydEQERUUgzdrntbwMA\n36f0Gf4wzewrCetuEs+Pc/e6KeyNpBotjhmiSZxdRwQyTc/S8oQBof1YQUP+\nxmSP\r\n=lH7Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB5wVtyHQ5lE+MbeChUe5aQUb7zbNAV/nbZubGNhC6LYAiEAkJYAu9mNl030QD2icE3/PzEIgtpqwpVePTXrYC2exqo="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.0_1620118609205_0.04927991088915884"},"_hasShrinkwrap":false},"1.0.1-canary.ko9sx9va.0":{"name":"plumier","version":"1.0.1-canary.ko9sx9va.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.ko9sx9va.0+7e8d4af","@plumier/generic-controller":"1.0.1-canary.ko9sx9va.0+7e8d4af","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"7e8d4af7b6163780d960e5820e5dc469878e93bd","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.ko9sx9va.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-Y2hUw7rdCS9VzvsgrqhpZ/vdoLJoBQvRLE0qaS70Q0FZ8LrA7D3Plmt7Nzv3060qbQR6HFp8Pjwif9h8XI9pgw==","shasum":"fe345d03c378419aaa3459006f48555534c6118f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.ko9sx9va.0.tgz","fileCount":13,"unpackedSize":28644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkQ1QCRA9TVsSAnZWagAAXoIP/1KoxRCzcSsr3Gyc/Joq\nLxrQJfMAvYhHqBit1GRH8kh/rT5DdYHaT1hzMzb8EE2F14fJ81sDVPZM9cck\nuxbXe3E/RSO/o15RMjrj15q+9Jc3RjbrTQr+LE9FdrCb8dD33LP7WrG0b/TJ\nxsGake7IJqs6ehKRgmKba5O83YIqCzeRtcVTvljpXBq0zqtpl8cveQl3HPRf\nAVz7ndlCz9KUQKAY29cP8J5CG9FKKKkwb63jpmkAi8Wv/U29SrgMuFVC8mw9\niB9jlaW8XIRyQbCqWLJCIigFMiZvE/GW/we+QO/BTyQpJW0D+Re0g1C3QJHL\nqL5SvIAY3834FGLSfO9Cwd1HWhX7BwAx7HvOVbMJ8WscLPt8V7M9i6o+u7/A\noHwUNdIFbmTwKB7hDnJ0yaDmgsa3nrjgr3LmuWYwyhCEEULNshZzM1C6zhrj\n1/2BeiiuO4hfVviX/7CGo+AqaRPv0XtIT7ouh6p6BmfxMXr2NcPLta9RN19L\njEVdtk+cWCZODpz5MnBQNkRDQpxeByD5h+/AdcAq//f9dEcGdOqrX2NeFfdi\nDl9p0GMoSx0HONZwD2bgLAKxUYrzZ8Be6lw4qNPBKxcSG7pJ4jt4vQ+Uuhe4\n+bNfeHEMQOX6f8K1xhY34ML5QO9JlfS+zCCWLkVI2onJ7iJ8tuPa96iiYlXo\nlXYx\r\n=42Gi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCwyA9PJJGbJRjwf2lJsAD720Ws7STQe3kbc6v6y8ncfgIhANya6/VAiTzKvglHPRFk7DOWyFr36Q017XhLfdbUSgiF"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.ko9sx9va.0_1620118864508_0.8343869421651222"},"_hasShrinkwrap":false},"1.0.1-canary.kogs41ax.0":{"name":"plumier","version":"1.0.1-canary.kogs41ax.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.kogs41ax.0+ee813e1","@plumier/generic-controller":"1.0.1-canary.kogs41ax.0+ee813e1","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"ee813e16e7a08dedb905c051bb66c31bd849ca9b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.kogs41ax.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-ya250k0FxNrwrYj/nxc0DB79m2xCLWdUGIZjtz/9Z2OBPNCsjG6LgXpInjpHKbfPYCXib2+K5++9ndXmuoHguA==","shasum":"59b48de2257b70b4b54d33aa9791c344889dcbe1","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.kogs41ax.0.tgz","fileCount":13,"unpackedSize":28644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgl31fCRA9TVsSAnZWagAA1rIQAKKekfwS0Ou6O2fn1RUB\nlpuHxkOlxDgamPus45e7ZV8g4kVLs4Dimif7Hk2pIq78n69JZ8MD5/xlTMl1\nDFoyR43y4kVbcUSGaKBxdEfEVakQmGEG5L1l45CiaWKOr2gRHmor7yXcSnn7\nQt/ILBIZGEdSlkEC/S2ETJj4jMfPGcn3DhZN0bIu9RSouxBnmm/WGc2sJE5G\nmFakmnzq0PERiDmG7U9ewFR7xmWbHqZGBtMlwmLRlnRRaMWRLb2q2TGfqkfv\nQzFrFevuKnTmNzTcnVKEGz2WY6XfqOKRyLj9szE5+0adLgG0if0siTDzyDEe\nlYb+l9iUIOCqkp97g8d6C7ENqyePFgaSMEiX7EQwinfezZfS6g6JCWAgweoX\n9DNJBHT9Umvouebobu3sO0njDlZh7gt0sSlntR/P2Rrv0ger0qQU6XR2VD4b\n8nQlhdFNlrcJ45Mkl+MO/XRu472g5xWZIXR56F2G1ZvmbrdM2gl0UzdaX4xe\nuoO0ZWXr2EZF3sifj/xsrLJxrOQ8OXlU0uYXLPW0Tcml3N8DgpjevWstj0jJ\nJCRJ+8rArPoToRmYIy20EyRXBm0nK1vRt9UnCZgjROy3nJX5fFYjJjJTtUCe\nKxQjwWpCc425e23mmOBvb1U3q1q04r0JqPZ2nYEjFfQW+XQ28HG0+vlmlzjB\nYLeI\r\n=SkSn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICy5/boJfdn1wu1GVATk4pTOv9ggxVVSi9+ZRwcM8qq6AiAhvmcZbdx41vtw4TXbXoNRmXJBQADGjO/2aiaCg7/NIg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.kogs41ax.0_1620540766786_0.2741310022480046"},"_hasShrinkwrap":false},"1.0.1-canary.0":{"name":"plumier","version":"1.0.1-canary.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.0+86c4311","@plumier/generic-controller":"1.0.1-canary.0+86c4311","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"86c43117b03b3474521573729b2516660605cef4","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-x2mutF9TKVb/BwAmdv02ogpWjm3DA+E9U2c1RRCjFicEy62tED2ddsiLnGILJczaXfgXzVmUeXB6u6CcdsaAcw==","shasum":"af1997db4f9fd6d9c345c8bb4d6ec6d581fa0d51","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.0.tgz","fileCount":13,"unpackedSize":28617,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgl5QJCRA9TVsSAnZWagAANmQP/jxv56T0hcEqNiUTnA/W\nt2qxf6JN9yPQ8a0F3FP2MlvTdpRj3X/uGv4vNkV6v+Hxc5yiU3U+pqG+Pg+/\n2VTAzbv4IXzxxlHGUAx48WdrhKhxK4KE23h6hR69eKv6iZhaEpO9YHjcHWM4\nsMPQQqDHUzFPSK8zdd72qHp4cDPU6RZjpastqVquFOf9ErUoCzh6HNEIVkuX\n4gNmax6NEKju2uZYIBdCnFyJKBHt3hHovleewFPF7Qxkcpxn/EIlhMsnqzLz\n3Gy040cWcdB4TXif1b3jLQ7i9arTLsmDW8Pt9plsx0xd0fvaoBOTiUFGbsRF\nnvcAvxjUbZjvxB2tUTV5U+rJyrGgVqRPgABGoQkREN+8nDE1etH8TZIOf/Wz\nkTFKW1GtNIa/TdxdEtkxhZxZjnYpKzFehGd0DeOnaxN20Leo/dDyUd5ogeyV\nx3BmewOlKewGm8aOJlIYneaN0STOqxNsv8hgrCE1Ey2c2qO3hXur0MGtvTCK\n57RanVfWS6qi24G1uVJlY90XxOsdg9YsKJ99KMEO+o0kHTlRHkw6FuZwHyI2\n6XNwk6e2bQQeT+0sGyo/kWZmy4ugAzBHQ35RH5b8BL09I06BGxxWMgVL8o1W\nIwqsQWO/mHv1AnJMnj3HsXH5gccarGpHNjHdGprdTU4DiiUsFDAyEm6CkK6d\ncCSo\r\n=Ekwa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHTPNwF6lQAf0eL0kjrSwaFIjokPfsH2wD7erlZ1oEO2AiEAkFY0wiLJUuOmEbC3cnTu3xsIoDxz8Bu6hgY80J0uuJ4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.0_1620546569371_0.578719866836132"},"_hasShrinkwrap":false},"1.0.1-canary.korp8h.0":{"name":"plumier","version":"1.0.1-canary.korp8h.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.korp8h.0+8987ceb","@plumier/generic-controller":"1.0.1-canary.korp8h.0+8987ceb","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"8987ceb782d99db41bd0587aadaa0415db2bf4cc","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.korp8h.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-fLgvC0xIvDP/V1raXNOwFaeTjp0haiav8QR6ltHuRedSBY7KqwtG6BA3PMm45uBwk3G5Rqop7tDOa1TdmpR9Og==","shasum":"03cc393e244c8260fea1b1d92f5393139cfa9099","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.korp8h.0.tgz","fileCount":13,"unpackedSize":28638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgoZCjCRA9TVsSAnZWagAAsNoP/0eCp1ebZ2dFWXxNFr5c\nJzMwSA9BL7ARcugg36cYgWQb7ZXBBXa8GgoxLCkGXrX2dDsFPbDRWHTJtLIN\nea0Kcpfwti/5Jx4fU+02o8FvA+CbW6wExQq8wcQPx71qMZa7APhO8ZcpZ/Ts\nbmkn7Tw9YuP1HKbFzm4Dpk3QD0iC6CNfNKgj/kako3AJwUNLLWMy7zbp4GYj\nP0P04xLX9SUglAJhBRJZnJGz7GVmzDTgxPNXpu1X5xGZRkRzdUd7lfqqQFx6\nzcXivYsAcDlccAPsq1SQ214ppuyyXLriuYpJS67rs8UdiHDrO7wgZ82NNQKB\nAkApsqRD8S9N0rbeJ305wU77ZIIb4FDIVo7vN5Z+1hOyRX93HYA6KAKMG4xR\n6bRbHmUzuU/nH9g1oxfv6usE901YBUNQiR/JrHi3ocfqXn2oLdEgMxv7Dk/x\nbf6To8p+4RDd2eg6zepVL6UWtMLcw0I1a8GtV46SsWQNS7gHcImun5pAJObf\ncQhUgH6J/7oOGylQRi4UD8tyb+eX46CBvDaXlQt3+W8PPZwKn+0jL0QFv5LR\navcyrXOBe7UdNvyBsF+FpLlaX+n4Kuy/SK2EAolPhSx2tsIrz6wjhlOG/Q7A\nNLXiLaOiuZeVfFKM55/2ExAKD0XlbwUDZC1TifVHGj72SCStD4kWDxuXEza/\nGRhp\r\n=AdhP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDO6GiZCUtxskFpRF5HPLmv9k3vKbPPNBR+tOAVKI6cIAiEA7hCMCVjFd/VGtSLUBlYQ+yHOrY5/FbrcMoTS2pI6yzQ="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.korp8h.0_1621201058857_0.2800429067804977"},"_hasShrinkwrap":false},"1.0.1-canary.korwha.0":{"name":"plumier","version":"1.0.1-canary.korwha.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.korwha.0+a0d79e7","@plumier/generic-controller":"1.0.1-canary.korwha.0+a0d79e7","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"a0d79e790154c4480fc0958bb4ed0eb43b1dc45e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.korwha.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-0e017TQAFIYZZpNL0WXrGSxZ0NBoz5FiztJvdZM/TQSBQ/TszPep+xJtJ36FhKprzr9LOOFFBL4xzH53Euo7Jg==","shasum":"19361ee977d2fd622a0e7a3b6a033794a1b02ea2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.korwha.0.tgz","fileCount":13,"unpackedSize":28638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgocAuCRA9TVsSAnZWagAAUegP/1+41gE+BTd+NiPuhcgc\n3r9V2LbP2Lpklzh+nsrOr1de8EP8y1kBJ73VCGxAHRaWJaFMaFEk//as3+8B\njMIgcorpm5CbbzqpNXIR1268fkdsXypGh++llhxDUiZ3XVx0KeaTwBMUTw5f\nOr/iV/laPErXHKmKUjJhmdgYTgHmclTvIQ4fprxVsyinTWxqX2UxQRHqJZhh\n/ScaXjGEW+jAyPyhQDUFRS0lvmpJKOil5knCtcm8yvINqTm4JZVaTepGcvCD\nfkNdVPhIcAGny2Ws+LGWayHPeRKuQ5ek9eGrjkZENb7nu7NtbJh2lQLvGDD+\n+aWXON3YhmygsO/iZ5KMxx1TBVmdWWB46vwxnb+LqtJrW2MX3OFSNDv278NO\nDR9t7y703Cn5ZqUFpV7zRVVewetXn9Wi2ga/sO8J92cbJR2OTsn8KCC8LFbc\nCjJHfL7xOWCsBIrt1RoJ3OX0CRGRJfHC4xTPi0kzphFBB302NQ/4QeLOeoMb\nEqLGAY4X6+0jR8UdBQeMw9k4YO7Djjbr3yfP5A+p8kId7LzJ61ocoDUtrlXi\nUl29uhqoKnTy1qJghDSRRYRFZ344cT1puoFOPjfCc31JU/Co73iGJT0Bbfab\nQPB3lA/fNjaRBtpVjbOLQ/jqclJ8jYOGUtn5nYvBo0iP3XTNvC98nUw4qoof\nMkJv\r\n=FLBA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGUknDlGsIpji2dbKtsUfN5XyfwLui7NEAPjiAxUieBOAiEA4UNXffp7b7zYhmSo4NsJP9Tdk7ch77N/4hSt9UrSOS8="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.korwha.0_1621213229605_0.4415512333145808"},"_hasShrinkwrap":false},"1.0.1":{"name":"plumier","version":"1.0.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"^1.0.1","@plumier/generic-controller":"^1.0.1","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"527a2c03a65e582541f40baaedcce595d6324fab","_id":"plumier@1.0.1","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-918Jl6wbouu7Bbu9MnHcVT7RNPxP+JDJiTmVe2PiakHQ9Qij1G7QRwhhS5Foxl/wJwo+ZGrRYalsBwN7bLtnyw==","shasum":"b4a27dde24f5fda46c49fc034e6aeaa5a2e81582","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1.tgz","fileCount":14,"unpackedSize":28806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgowpRCRA9TVsSAnZWagAA1XIP/iOv3GUB2Qti1OWD5EEl\ndBuQ+Q2puRCfqC1ta9uvPRDK4gx/On7q9r0TXHrIed0hyO+IQvqXdXSO0kE6\nMJLT6q64dzXqIXcI0kPSpmxq1WpXW5BprkwKZiN6DWwaTxUBcjPHD3PV5+Y4\nPCDmKg4mVcnaY5pUiQs9bPG+ufXhAH4o/hhP7CxaHZf9QpuV51wlvz31M9It\ntkA0juuSEIajfal78TfRj5rI5znKXlEMAPZ/zU2U3EQfIULorC+Kq4m/6uZV\nIqQBl5gZBihhvflYV6AJSCVHoWuwU/Qeb5Ea/9WndRrhiRKClcm4Ig6Ax0Ck\nXK9N4w1xdmONgLTF4ViZbzd6paqr7z92UoQbg9IaoafNbP2oTrJl37ZV6Hxy\nLmD6gFOjUc0pshB6cn0a1LSI8yb+Z5JR/mOR4BlOubBvGIwYV+/KjE16PyJ0\n6KPce8j7bbyhegrFhzqhq3vTfMj3pUen/FpxYYz+kA8Xxw9H6Gon2AJN/Tc3\nRoWjijOaagi5Rwb47IPr1tp9h3qocFPpIEbSBVn3RN3nl6R74SDPTNsWRiYM\nkSxf39D/vuaXf+zzpvSRkLfm9ez3TQKFQUUEvsbczFG5gjoTYO/Pgz9vVmvI\nsXztyQMbZlQwZLrpbPS2cyyeasWjh2F/xPUESIJ7FnnTv31I1bptkuuI1tbI\nSaVr\r\n=ugu7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBaDkycxJpV44ady+Sqaf1Idocss36g5JS1Hw+r8qs/mAiAJQgT01W9H4VMafhzTmL+MXPdTz4YX1E2ZhYiQIRfjmw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1_1621297745282_0.12058565948360456"},"_hasShrinkwrap":false},"1.0.2-canary.kotc3q.0":{"name":"plumier","version":"1.0.2-canary.kotc3q.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.kotc3q.0+527a2c0","@plumier/generic-controller":"1.0.2-canary.kotc3q.0+527a2c0","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"527a2c03a65e582541f40baaedcce595d6324fab","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.kotc3q.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-a00yuzJfYKp9foJH3dyHmJKXNOmpWac1e8jq/yP8ypwS4+2nf/Es0OOp66XfC+h2ccoqhxOGYzhH/XmIHsDLLg==","shasum":"3f9e060b0a88ded57f192ad7e60eb835baf8fa77","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.kotc3q.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgoxLgCRA9TVsSAnZWagAAScgQAKRWuumMm4DnAFzgx9h/\nYjC+i9MXRm0RgUtjYFiXOy+YDBuqOnXTYSg4X7+74m6S04RfBq5+mfn16VHV\nnDgT35xQsHmVlVv4FsdOhOXQYVPxaAeAjQpF7UoYO6tby89VeQjBK/9V4f5e\nr3WjIr87HC61IJT1zttvgBAFCvsfg7eK+kWCxQW3TBD7rdFw8foUvDAdYfxc\n/8cOZQi0xRM6RvZXhgg0SUFdlwI8JE/SVsPc95RS2RW0rjN75pvDu9VR5Lrn\nWtk5g0DHS5zw8mP67dLndzRNQKm9YRmSlN8YU8kw+O5NnL4EFMmQ3GhvuNqn\nENm7ppRZrNh4IZUQuI8XtbHJtnRXqZHSUE4S9Ya3yrVqLr3l6PLwi3VgMn/E\nUJvav9wolosd7q17cwrBlawDi9yZGXd26RQdyh1cZiHpyaEh9ZDC+4m8JFpr\nRFfCLYpIUF59RF4gYyKrgWTXvjU1MI3M6ZqGQKZWF0VFkA4SS2KCE+zE7+qq\nxJTsxP9og6xXhcguujNI9Ob4yCO+ogZzlp59MK7SR6iAqk4WKTSZ4qiyUS/u\nrgxx+dlkrian02UQ+VayLkHicY3g4mfnAqhdwT0XZnX+Pf/0dr1a+SJpt5fo\n6sNxIeaqTfya86Z4+UejXApCWUcnB7djBo2AkhMOYZm2jyKHmQiegste9h+Z\nXD4w\r\n=1x3b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYiEGufceNs6QFVpuBmTnUxf2fT5Ak2RrHJmeBdd7awwIgWMg8bIfJn+8UdWpgsccspp7ILiA/hZRcAqNNMlJJNCA="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.kotc3q.0_1621299936150_0.5609097019272786"},"_hasShrinkwrap":false},"1.0.2-canary.koxlv5.0":{"name":"plumier","version":"1.0.2-canary.koxlv5.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxlv5.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxlv5.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxlv5.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-Qp8qWnx2OSxFnvSQq4D+IjJxFjtPHmC68K1ufX00Q7QJwGF7r0K9IJ5eFgM+e6cIDBqf40TmcNgIrFJQpvTThQ==","shasum":"eee66a78bec0a6ba51204e89e6a23d8c237b756e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxlv5.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpwO4CRA9TVsSAnZWagAAPnMP/RMHJWHdeBVJP0etNqPR\n7SwZYyrlqyvyOZ+CfujCYAxQJi3TFB++UL/+4BlGTvjPgHZo4QIB9p6NqRhU\n2sjR3JTx/hiz05/KTlxSP2u42e1CyGhzKvmfrmQfTY/XAUipViGKXSBKe84p\nE4fdYpTcbPian4hnTd7BcjtuRxQsQKli87iRivk7RnmeIiUN7wWrQpqKrz3c\nXSTBdNSSdfVjvgSvKcqWg12HXJOGgzqbrgkhVSOj4b1VKvgXdVgzxYRRzq/5\npx1dCc2Oe0z0ZpbqFFoGxyO66D6q1/i9ulY9nF9t4GjK0c9kWq111mA7iOHZ\nClLeZpXHcp0ec/RmErW3fd0XAvodciIU0kniA89kCsd2L6KHxI3ELuI4asb9\n7HvWcjkTuupB2cQ0AFmYLy9vYZpi/cBJCnOdfMeHdMMTitGiRVLkH1nzdMkq\n8L5LwgaRsl8RAZ+GPNOOEbnFT0XNVjnFF6y69sfd+1/yv4Y82gYgAVHKFkid\nZszWmhOAAqcXnZq3lfGYOU3MrujvCUSvyQOybp8dff2MCH8+rjKx6/o5WKya\n76H7UneHaRWXV3SLzvMSsHwhj2X0fxmx4qZN42tgm4UaVRjuHJnFdLlTZxQx\nvfomlVDzHhMpyM4HOgExP/Gis57DOgefvAIN6wjcAB/E6mp3fwA0Qo4Difug\n8CUs\r\n=PAe/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFqQ4pLZCBpIv3yNBZQqr0V/bK7PhyA0203sgdBQokZOAiEA/ypoil1W32Z+2/eBzkU3qaslvaGEk/EvavWp/65LTCw="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxlv5.0_1621558199955_0.06732752027751099"},"_hasShrinkwrap":false},"1.0.2-canary.koxomt.0":{"name":"plumier","version":"1.0.2-canary.koxomt.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxomt.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxomt.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxomt.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-tI+gU7MspuR4vFN1oaVops3BpL9fzImep/hgxHPPJhz+U8nge6McwMLJ8w2gYQb1ejRV+jkXbnFV3kcvAj/I6Q==","shasum":"186e8f854d054723f9227d58ca5b85bd04f6cf53","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxomt.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpxXeCRA9TVsSAnZWagAAGe4P/3+cB7wwzhndoH5l/k7t\nFJ9qBnD125haamZjG5zUuPvJUVGl9EiDhFJia7d+O3IZ5JkgQPtoXsXzaFHz\nVD6nFG/kIfNzoIadHwfSB8O/YhYx9V9j3K8jtIRJxUNc9MLu8u/Eu5CZ7zDD\nfqjsU84ZHcUBMWaJu07QB3jOxiIHFbKQ7hEmGM/I82KmfoPcQmEPkqLDOu90\n5EYIFUb1wZAo9dMV5k7BGtMMRoC7QnQ2pHhSsRqHx4Ym5UE3GolUw+KeYDOr\nyydTbQX1wcBi2hAD0khpZD36Vn2hsEPXqbVWNTtebmldQRNDJt3GyExtYtIp\n61VQkWmNh1DJuN924vYCEMDcMOPY4+QrrgByQfGqKwtwvxomc69/7ZusDXzO\nK6ac2HahpN9BGfoU6wjMcmva/koP/SnNR3WOO0AP0fyE1FjqHUUGZCr9eF5I\nsz4Srf0fLoETb3D4WcColFjLwthpsevnsuzs23baNkR8EIhiWguy7khGnvtP\nXBrD6D8qGcwE1YFkZM9fXJkbHxRMXtYUl7wprjWGy1iwjU3KH248HhWXoy7w\nzlI/nP8rkM//97aO/GJw+Nv6QsuunBLXjc378wQ5oN1FGQR11B/F+5wEb/sU\nA/xx1GUe69/GGe4rNWV/V60gb/YF8j8SfQdeymTsAtz0+EsPxIjOETQSgQuW\nG48J\r\n=+0ZM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHGn8ZE6IDprnFqVFo3NYr9yrGi7IhoWtlV7hEDl47AoAiEAwlHI/jWvL1qMDQ0cgMB8Rev4sjAqt7NI+S+TgXQ/Ri4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxomt.0_1621562845712_0.19471889148354582"},"_hasShrinkwrap":false},"1.0.2-canary.koxqgm.0":{"name":"plumier","version":"1.0.2-canary.koxqgm.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxqgm.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxqgm.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxqgm.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-tNOtoqvn9Ru5urzjwL75mSJPfqyV1gXBmECY0iIABfPUmdVh8qVrfoKJBlWocvdFNIRS2gz+xSmd8ErVPq7OMw==","shasum":"e1a73aa058fbde92f091b043fe4140221b1cdd9f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxqgm.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpyHeCRA9TVsSAnZWagAAMXkP/3358WqWgL73qtyv5wxa\nBpp0zDktVn8LKI5Y6eakp06YM1fi9czOQgae/7UxURCQu4CkRc0tJHA2bOhv\nxCqvBNoSrWk2eKWDrj+3/5wd6RG/BCCAhB4yfDV2EfCkeWfk4yXl7Ag6eTXc\nq0XaSf2N7jk3SoBm8vvQBlm9tRLKpG4wnZh0txubzf1QYOlsNvQ6jQGWk+nf\n/+u3J0FqgT1zJltORkIkPTpA00UGAqTN75JIaloPJnMGnJ8rRVTAHZFnZ2zt\nhYvjPsCrTK2Q3U+go4OP2eWVMOxSHWngayDUIuQoLWVso3cuK6d00G+7dsnT\nQ2aSh302VuwJ4Q7hqA1DoJZMKFTCq/tUJWCXfZm4R62ccOQikzu5JMpRDc9y\neNSo6cb0Y762nO7eVKI5XUXptqs2hg9WqSxlUBGSB8iahCTZv5S2o51+tnyZ\nH7FNvdtXmnjsa6CpkQJZrraSQhbDaAAiz3iyQgCIEf4lCT+CCZK7yMcpP5CI\nFmzFDRFkbH4jRmlCyBkRMchhaODqjFatbrmNyrXG5xKg/Gv23XtkS4Cbxozy\n56rSEBmyvPnhglEkjbYX3QO0/kURoewaIslgfGfYdmhkBMX3HZgTvLMGg22U\nRvzskk+x5kfVohs8za6O3kK2zWQZT+MI4mAuGx2+U6zD7ZV7A7Z3HDSCrWg9\nziaq\r\n=IV5Z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEjEmJJGd5KgU+8vvvdpVyJ56/cT5Tg7yiOftBizeJumAiEAxYdv6JAVnugiaEwJtVeNyhM0UCI5SwsAUlTRreXCY+8="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxqgm.0_1621565917938_0.7474286443711566"},"_hasShrinkwrap":false},"1.0.2-canary.koxrd3.0":{"name":"plumier","version":"1.0.2-canary.koxrd3.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxrd3.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxrd3.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxrd3.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-ESlFwcJZr2Biitp1CtGCEGoQuIyqzQ5/MSdC69m8ZmSsS75K+SQOiAbSe1kUgv396dBGrZNSZYWfOTMrHB+qbg==","shasum":"b370b5bae9003f8aadba71827802355742ccc1eb","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxrd3.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpyfICRA9TVsSAnZWagAAicUP/3gsrc3Yj0ioHrBtsvOl\n4WITXyXodJ5xBYYZ7/Nv/jw5PqgnIG2uP+E+1AvIj1k9RiZFfvwR15WdJbzr\nTfk/d4/rY63mjmj8pkym5qwFdbNKHL/mcYx15QOF+vMI7N40RfSZ0I++hOvB\n9QkfpJZ8gubb3Z8tRAIDM5n4M415VzNQtPK6sGlfmVfZbnmc2z5WHHA/uJT2\ncW+BHwftN7Sg1QBzj5Hu1Bfd781kcS0coqRno0ISUI+B1bCtwXbaLoaZWIz6\nQ4KPPuV8Qvy1YCxi5XNR+mQrCXIBbO4KsGxOfw2EJTF7RLrJWchw800CV4Oi\nVidBmCnxdwcNGz/BwBeQC3zA2zPtgvdQDjaoHw+88gCeuB2qmmjgdKr2/+PW\nYPU+EG+n/Qjtz+ZzZCJFJCaUBuos23VHqgv/nTBYlM5DJo6Ax/OZXFk9TKvr\n+x0sYxFJiwE2TX/KeZFPomnbRE387YOggKEHIUjKExrTxpydRV+WRSYCHOLc\nttR1pyaw27h/HyC6QRfkyA9VRwD4ZySkM+6pLOOwIL/ajGQMl1RjwiDTKWrj\nO1vWduzrps5RqXs7n9EC+oz8GTt2cKEtnzNj94FSHqdHMEYjyAZzS5ay0mrK\nZ5r7wJUvIEVaPF/Bw8CT1kUSVSUGVAis+yTmhZASjYKvHgeYpmtA/Sii3fCU\nUxSt\r\n=zan6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICYmpYR7gvU/fnHjrAn36MpzTcgCZ70u+B0ZY5yHUUjZAiAsJ74A64hOr/5eVfw5KUJHO64v/ooHlLK+oUmD9Z4tww=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxrd3.0_1621567432354_0.8086409123600804"},"_hasShrinkwrap":false},"1.0.2-canary.koxsli.0":{"name":"plumier","version":"1.0.2-canary.koxsli.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxsli.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxsli.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxsli.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-pW7t4lA4LXhf/ED2ha+h5UVnHDa5GTgSWLM9WK1bcSMWbKDPCE1y1hNkfQEDamaSvdCsotEWj4tGdydEohr2EA==","shasum":"1aef5a5e5ec419a08efe1db51ef58e3c3ffaa713","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxsli.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpy/hCRA9TVsSAnZWagAAejoP/2WU3SaB0ja2hZew1YFG\n5hfKrJOyPrOIR1hCi0qm0Hi2Tat0q9QJJjA/CbatcobXDTyFluFSFSDUAz8j\nbWndhLJ0p5PBnADMlcR9eDLjd8Qm9EkHh/RBR9I4xPjEFGwp53tUvccuNfgO\ndgCzHjx3+nwDWVM4PLIgEIQR/FlM9ouZCR17NK+QqAqHVhY0Otqo2mWoNqnD\n/9gNSFj3p5m7/uYBTGYD0IsShHxo8bT4lJjtCbjND4fyO/LY+CMlRnBeniCn\nCnBIP4E0kjZO2N++Wvo8UHpHP3u9tX/wO4y2k0on7//vr2LbYc+8ASanbugB\nr+yecK4ChegL9j2XuY9JtHQVAJ+43z//suD5uVOaGVbKqb+15GFMXpxtjZtE\nr2LA9ubb8BUZscnebjOL3NiaWWpuDfdNMCj9C1bmRSCiky1KpCJsCohvJoWN\nMNEfYhhJPpQKbPUZQeeoryZO2QxMvFI2+QEAAZncuvw7v8nAxG6XZfc29PNY\nBo75pzWRASCn5TGZdaScI6dDGqg8Xgp4xoQI5DlSkpRxEhgRghZ8i2oAGxFl\ncHkadMtZTNwMGWriyzasOQroMgdNnkfSau0V7qeX7+e0vKda/Rn6Gy4JtDPE\nFMLbsQmiSp83XfL0E0Vq2F4NKnzZIZu6EqZSccKOutcorPSsuHyBGKbA23Bt\nYIqF\r\n=zDVr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDJ/TkFDcTaotzLJr6CazYEtNlMvRVwaf4yIiIAuOKk3wIgWFsX0lS1VCCE3Z3Bsn0T9vEHhQPv+TGbxgFH4Mpb2AY="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxsli.0_1621569505280_0.43308320662356836"},"_hasShrinkwrap":false},"1.0.2-canary.koxtci.0":{"name":"plumier","version":"1.0.2-canary.koxtci.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxtci.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxtci.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxtci.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-KPPadgBy9uJrxpi7Kh/RZsODqiG+Q2qWzG5QZ53CUyQA4Ay3OkrZJLZFavvlbfVCZcfgB/2HppyU4tkk4G+/TQ==","shasum":"ddc3d8ccc366ab9978e02f270f128bddc3c355bd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxtci.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpzTLCRA9TVsSAnZWagAApdQP/3uDf+ncBstMFDQDvch9\n09i1IsfNfqsU0yFPoobVlymZo4ClkiwepgCp4rLYKcZHRSRcqSoPsakhCPND\nRPuy4aZCAtTK5nt/tocWGBbL3kVWCFKMuYtn80LTAqOXR171jgE0V5IUWHnl\nA7J2HioJxs32YawsMrG/NgGOObFcq3GJOpXnjbhzNSCENg4ptYM/19mDYbO1\neKTb5mMF24ka3CUIJ4Tzefey03kFocsn1wuEEqJO2zgCZXpAuH5tNukVaVbe\nAH939EOsNHXF1j2Ac3dP9/QMdFhPvKFvAsqJP8hb/GgVaytzC+RaFLBVsCEI\njdKqKxTBWlRuaU+e1hyUFbzppjn1Bz9p08KNuDK3Z5WLimy6jb6Z5pqK6kLZ\n4dAgriYMdXccR78CA5vnBReAMAUPA0HMXuQV4NmyCz8HLoQHeXbhTBgxJCSt\nB+K9/jhlqCmt4YzMFhgt+rkQtp3QR/Pb0ZiBJsnE0kNH36drroXkZIVzHDux\nBIEzftBljDLwiGQ/g5f0o1O/fvuprVooFpVpF7sRPWtmolXiTW51rAC8Qgfr\nCg+ZyNB4w1afO5dXCB3YOahQy5FcEXa58AkFo+SE5zPYExSHlFh1+uGcFQee\nm0v8vkZyGN2MAlozbcgnZByQYwLd98Ucdfo5wxY3TQtZaRBpHohjXu304WUJ\nPpDi\r\n=6SJg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHLg4Lp0QfwNHLm4ctOa5vvUUozVQWQd4VN1zrZ+dJdsAiA4+fYTc8azaTDkEUgPQES/oiuK5008aohgEcMOMCEWKw=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxtci.0_1621570762613_0.5023197846551286"},"_hasShrinkwrap":false},"1.0.2-canary.koxuq1.0":{"name":"plumier","version":"1.0.2-canary.koxuq1.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxuq1.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxuq1.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxuq1.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-QFoJdIA87M2cTNGJEXXfxzwZiU95U4NpllwN1gwOCUyN+34SFdZTYFrfSC4YymlZWdu8i6Jf0mIsSKJXWCm5dQ==","shasum":"a30d153c277471039ef0a462cba3cee9188226d8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxuq1.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpz3oCRA9TVsSAnZWagAAIFEQAKEmYpyxKBI0tASi9BDm\nzRBgR3gJJQXTzktW2Dksqo04CDORwfQNmrm1lVy3IyWevfH1qQcyx9RKjNH1\n5fC5mbv5+VisRWPJ78p/RIEU3jE15g46xnvYkWFJQahGPN/SaRHKIQUHvdFh\nBfXXPwX+GuS3/C6Il/EDJfYms1clJGtb6jFrt+8e2BjPkbrbCpkwqeZhCRsu\n8PQrhbMOCtDuZ/u10O93tsI7oCrIdf1d42ucZlLgqVV5xDxsHsi249qhcvEx\nopmrllC/xL+L8k86oz3g484T76ZsLnxYl0xoC54gPIh47wnyUMIO+suKoFZn\nZiIDTYYHxNgbqGCyinm8TPtMkZevzp5h46Idbv76Ol0fufASX2HQIjEgmUv5\n+AmeJSImX3Jb9fgd5kWwAUzeG6/jRnO204by08/54FM9Hb001DanPyoMx2IQ\n1xxU5hyFyacx8/hj1H3PaQdT8xE8FifC7d/ohUA+hIdM3t6ScnH2BeEvjMkv\nA3LnaMgm3XhFYMsAeofOveb7rDVZHUhJHEi6kZxuH8y+YBAEd3pn9DxAVFdp\nTo4jgpj/FMdXOrgnEXBgFr3H7pi+2oZzPQixnB4dkKHS8BxgcrOHjBupx6Wi\njE3djvz4D2oFJ9RlCygtBAIzsGrth3zerpFNv3GElSRgKPFnn6v9lxKUETFU\nsIk/\r\n=Bbbv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCfcwfcI9D95VhVGVv9t74MzdxKDDG4srRi5olpmd0smQIhAMtk4vXEhw1lCuepw9v8x1iaTtz4TxXsTRNbKHVG/y/9"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxuq1.0_1621573095611_0.5791470166678894"},"_hasShrinkwrap":false},"1.0.2-canary.koxvj6.0":{"name":"plumier","version":"1.0.2-canary.koxvj6.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxvj6.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxvj6.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxvj6.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-4UmeJccEbYGgbaAmyZhcWLNmOdPOvP3qsA87BRoZMcYdMQF78s2WDyWrYGzj2a66W0WYCACQpm0WKvjCIWKttw==","shasum":"09b48e2bb1ada1dc06903f1105a418a6f8c8af59","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxvj6.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp0MkCRA9TVsSAnZWagAAbCsP/RH+o0cJIbtAr7cwpuZR\nR2yAdWOgcO5F0yB6FpZs96Z2RFv5yogxh4+S6VQWg9RAE9LZ0bIfboBXdj/p\nwhTqyI6ICKhWQkvHmVdDDUOAMb41EOFiEwI73uee7TY429+mf9qP4g8aUTkT\nQert1Z+NhH24GJ3Nakc9YZ4270zuvfBM/tTKcmK1kh2oqe0mUogj8E9hhUxt\n+QI1eFrubf93oNqZDayj6SyLWr0tfPYaVSbhCxtDpKlAf4nIO1Vod3em7NT5\nB1B7G/NQVBzZJl76TI0N58gSUv1WXDv5aaipYOhok6pOEHRwmzPOOV+OvcUE\nvufyuokKd17vezouqROL9BgSTM014j275SAvMAn2jVbbguU/ctNdQOVfLT+4\n32ARAUvWHX/HpsipUBrJhtthnLCkwqkMATnnYefD+jgxOclF2JlIfsbnFb+1\nFHD9Ptzg6NxlZ8Urv8BbfL8AmUdErlO0LomrhcMe9Y8HHCqXx3cBeNttZHDa\nmDkHQzGy3yWQmvHaav5+qLKN143Xc3M6PsOxGeZp36FqmvZ7/zg6D44buts6\n7LQAv+olWplg4MJapPf5m8eAJ0wQKjL2lQwa9ykqWcYdT5ovZhGjpmNiLuYy\nL9qTEugm7rwFYRzbPLXdUEyLTZil/fWwXz0D6Ko1tKDtWyARxV+Mzhx0GLp5\nVZCk\r\n=jago\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVIJLaHZTzBMbF9aXgL/dClLzrGrVAA0JPILcq0X1Y1wIgdwVIp/L7xYYFsO0xKl4f2zIuk0lBSAvmNvChgGJOCmM="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxvj6.0_1621574435752_0.2840703502009163"},"_hasShrinkwrap":false},"1.0.2-canary.koxwne.0":{"name":"plumier","version":"1.0.2-canary.koxwne.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxwne.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxwne.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxwne.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-la4rNWPfL5Sh9xrvxLea6tD2Nqcgah1O0hhDwV3ON7cDHFl2bVJoVbjJ8mELXyrGa9TwoPMhLV1t9i8lwR6bBw==","shasum":"62e2dc7a305bfb176bf540e798c2a594a79068fb","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxwne.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp0p6CRA9TVsSAnZWagAAFpwP/03rWXYgAQSiuiZp353M\n+AktshZpg0h38pTh/nfNlCacgGRRZd74sLkHE5KjSVHuBFEe/TrX0OqOxesz\nFq57JiXA4KLSgK4WK+FB31JL+ydcfJbe6sLiyItutZr+mGwueizeR028RDj4\ns7CKcqg8fgzmiiYMF+n+3u4hjBdUK4DP1vmTfCnbV4zzywv5GG2qm5DEt//5\nSqo0p+VQfhmXwbjd84KhQxQJMFU0eDh98xEqI/UZxDHULm/cJ9HCD+gxhWLr\njOAVOHo1QW7AmGbR3nL6yLjIjCIk62h8atWF5HeDptRP0I3KRWBu9Eu4+pRu\ntkwdXjcS3nzAGhVXd4HSJV1VafFIVSfe3E0Io8C+BJeZi6g2pJ9Hit1TcMCz\ndDrNemgfWqQgg/QhFPkS227NpmlcW2I5/+08TROprqOoGOFwNzVllHkvPtPF\nLG3sMsx3ngsyw4VKUG1pD6l9S7DcI44ZU7d27JmW/OGdFbTQlAeHww24x7Tt\nHI2qW1Idz2l20io5J95lStLBTxWAfV/DHvqEpvMCFMHrXPAxJRQoyvdipo0i\n6LX3Bsp4pjLGe4L8IvddwB+PweN3ePGqqAaReJJ1Y8O7hxaO/pGBCKzOoZFd\ndQLXlt1CWAFWKvv+T1H6Ve7u6v10mDQA2vuj5VEuf4YXshHmFYnZC9WGYG0/\nWMdn\r\n=7NzJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCrVGbUwvw3tWquJ3mVgVIsxS8myLtA8Ur2wAZlerEJtwIhAPJhJB3j4UwZDrNBlzCJ0n8TF1hq0qcCe/YplFzImmMt"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxwne.0_1621576313906_0.2376374494835818"},"_hasShrinkwrap":false},"1.0.2-canary.koxx1j.0":{"name":"plumier","version":"1.0.2-canary.koxx1j.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.koxx1j.0+cddbaec","@plumier/generic-controller":"1.0.2-canary.koxx1j.0+cddbaec","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cddbaec69db9c7fbaf144256c44f76a081f8a7c6","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.koxx1j.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-W9cfrhnukQJSZSUJTpSQaQYSNFjsUDXhrwTKl23oGFOcTasJDWxhGk8PBacXMNYVuU/PnKhT7aZh21huE0Xj/A==","shasum":"6748b411cf2da55563c9e6d2f70cf1b7e96a2139","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.koxx1j.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp00QCRA9TVsSAnZWagAAuA4QAJky6EwIzufUMv9TU6w8\n7iYIyMLmTFFBAQI3tK9jaMyVl84a98h2muNHYkipmYbYmSnd/T3Hhb6EHeCl\nlKeJySE55kkuoy7z9kOcE6zgSnXYogAGvPSVoiD1Z52jA91B87B4NyHcFX7h\n9fquOyuKUXIOG1j1f2c56QauObvS0k+aFtMjTNhAs98NveZAOVyeHnpJGJzR\nyijYdqWfGJvPb/t9APBMM9xf/8bPRskmkcenslEc2S0ApRl+NjsIlSoxQL1r\nmchiF6EpxecJsFzO2XeRTyaPdBUFfQ0JS5zEn0JW9d2DADxeq3eVQRUriQFu\nJSSSzaz7BL2Tjesx9QIH9zmEvtS2gMlQZZF8Aq7+8WcvYDQ0jtPKdxb23jRD\n6ics4FM/Sc5YU4e3OSzxy2rZWZEDT3vzO8xAnVBHYbKZbd15C7oE+ag+MPWz\n1dAdqNYaUSPXaYg34hkyJAh2VwSNFojonjXNSRywQ4Aw0cNKade39E+74lmT\n2wE7w8XwJY8Fp+6GB4kq7jezDeXBqXI+KVUw9neYvBOlLBHZA++GI9M2X4to\n9u5ubn6MiYErxQtLwHv8p3iJk50Ct3fkHTbXX1G7nEZ/8nXGkFu+oxWj9Smu\nG005RA3se4+bxbEBJ+jtn2TlyZY9epujID9bH2591ERrEIVfR01Wnp/AJM2V\n7T/a\r\n=KPHI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAS+XPVYDfnUIO8stxMSzqlknysunuyTNQST+MAg+WMjAiEAtG1Sbt2Qixlsr3hWtThNxU97+QSTeKTXfJ1VYo0GpIw="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.koxx1j.0_1621576975714_0.5532441987407242"},"_hasShrinkwrap":false},"1.0.2-canary.kp2a4t.0":{"name":"plumier","version":"1.0.2-canary.kp2a4t.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.2-canary.kp2a4t.0+05cd377","@plumier/generic-controller":"1.0.2-canary.kp2a4t.0+05cd377","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"05cd377823e789e1c18c3902cad69798f196549e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.2-canary.kp2a4t.0","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-mAQTu/a/G/mK8R/ZkW7kxBGviCkzwrfbFBpfc/nsSqdLN0thKw45BGFKkAuxJbyLYtqX8gYZrolW3Ff6nW0XmQ==","shasum":"e5d00fc252db8bc6c7c4664d190272153e661f30","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2-canary.kp2a4t.0.tgz","fileCount":14,"unpackedSize":28876,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq1O3CRA9TVsSAnZWagAAYacQAIhIPWY4N7mwn9oYkSLz\ngw15FTTmMCFDXxvHy5ZsMAUBSu5RIQZJ5+a9BbtxNUy/F2nnX9wmSMdwABlG\nIHO5T/QQ9AMmEmMCCBIgBuGDIynFaWA8wWNi95XflJ9LosEyN0R/StakwBne\nlfVXlVsEtXxViNeZnpzWlun0JkKiQNVHnfUYtWBfa5ezqrnNQTFgHSWeyFCL\n2mir038uGSQ7MU2KZB0Qc51ycop8n+ypFow7Uep3+eTtcWVqBX6YxzYbspE9\nU1xxfvRJq8H/PjWr7AaaltX/NXUrpk5AqdJReDkusf3ZOnyC33yUnKGunfqP\nEQH88LM3KrFKQvHgryVYY4zOQcthHpOU3tAR9hSSXhuUiqSE8sfvS08Lt/VM\nlFrLH2aqRAxt0URtyP8Kqo1imEArOHLkMYk9j3q96l5B7IsmG7kgvYYZKIMI\nejHensFtK3hMh3LxU6UnLj8gF9Qg79BOaTUgoc/vYr+2TrjCswizVTECkmO7\nHsELJvGDtwddj65UfQ0NpxibW1e8TYcpbRNEyww/iygWLcv4HvjcHrY/Y51c\neSzl9l/ZeB1yiByASHVeSGzZdVN677K+E1SIKJ6NrlUTr/8AB9OPNHZU+wd9\nq6r39+CASYBfX8xrVEVLiRifOKXjPeLJiYjbg1TujFiz9DS500FTWiVzt2L4\nLvxM\r\n=Dmed\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJVHUGgPgEvpykHXXZVLDxMFRGD9MUPHkrc/rJzoJ65gIhAPBonTOHmyTsnmgVp7NFiho7GplMNvXKnNaCSYhRET7j"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2-canary.kp2a4t.0_1621840823266_0.05144765545373842"},"_hasShrinkwrap":false},"1.0.1-canary.22":{"name":"plumier","version":"1.0.1-canary.22","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.22+1ce9220","@plumier/generic-controller":"1.0.1-canary.22+1ce9220","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1ce922097ba707499a67b537e8cef48681c2060f","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.22","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-Z0EFHuZhT2CEtZzzf5IKEZm09r2BARd9p2HIHbvRgHDuwC5i4PriaeZgXgEhF5r6Sn081VJV1QArPm5ljR3KKQ==","shasum":"740c47e9fad61c9075adfce419305f6dd981c6bd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.22.tgz","fileCount":14,"unpackedSize":28858,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq1goCRA9TVsSAnZWagAApvEP+QH/2gaWb9vlfxIMlTYU\n7EZcPQ1fyTz9Mlc2SWLVMYV4IwmansVDIH9e031mLz47KGg03ze6ad78AiiE\nDmJ65k/V0nJJAS7y/lJnZHx9W10SW1MrgRJUWuM4ZFT9RVh7Bx/rogCphF0E\nTPNROUatXULjnZ2luPTPWSU03KNr8iJLS8eQwatS1y8+129ojf20NjYbdBtQ\nLjLtSiINF8ql1Uzk1sWsUxYcf87hn6wT0XGlCx6d5qLomGAi/M9uI5DKVjMg\n287ME1LU1YCQkdFLQ137Q+NeAkmmk9ONKz4fwMsniY0CmrqB+h9O1eHvb2PK\nfGPqe0pindLoENZw7Ferbfa/F6Hy0SgqDhwAotZ/tcsgRJB1Z5O6XqFd2l1I\nARDxNw6SlGhXWPh+mQbKgjMUNNUkANdHznR8Nr+wpKnGfPGFbqtezxPPYnJf\n9OIdRMAKrug24TqRe82HvatyR8jFIe42UDVHKTOMsHFdYsGQE3gmSj1cSPOF\nP/PYnbW/f5EAfF5y4nfV2pGe8wGTlGurHYm2Zss7PrK9+F+wtC5Xi/DRRRtj\niBve9SGE2dn96M6jjyXqTVxFQWHScG9WJxQLdyecIGS9PQQyI/n/UgaYsr9H\nLDkdFygqcMDIn6ISE+1mv8kHJ9vZOyzTu4N612Tm81N1tL4onmPiRUnMNUEF\nnTRX\r\n=WNMT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICSwV6o3swtH56YEcD+YH2EtiMO4coyowFX5eHklzv2jAiA2IwbMMRKancZvYwF1YSIcGlABJCsjkFfWkeTSCG0P3w=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.22_1621841960286_0.9655616112513095"},"_hasShrinkwrap":false},"1.0.1-canary.26":{"name":"plumier","version":"1.0.1-canary.26","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.26+e22f8cd","@plumier/generic-controller":"1.0.1-canary.26+e22f8cd","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e22f8cd22093f8606b1f34acc2f095b02d5b89d3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.26","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-r79tKJxP5hAlSg+GlDRyRj26gdJRL6qfOqC+YR5xyrAdQ4cwzTFhryAQeACG9sTB7AUazij52fip+VqWUO+CKw==","shasum":"71b64928e303747bfd49dc4a1751c33af54dc363","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.26.tgz","fileCount":14,"unpackedSize":28866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgrMzICRA9TVsSAnZWagAApGEQAI6ceOyNs0bCrrmaU2nc\nO1GDewNYcjkeqQh2m3TDfka4+JjOrFeLo8O+0dpnQptMCPS/31foRbGQSMz0\ngzP8HbZtgRzZhNHlnjyGqlxgukmUNlJlYxTREBwuDhD30LvTnlT+DcSP9xDK\nJF1C8hYNpPmoNh0CDLmiIQBr51bQC0OlqAe51VBYP+oMInYCS5WyWJFBv9rM\nrPNFg80WgQ05+YKQpQcL2QhwroFjIsFStRlvEgZlMWi2f2KHuxWhXb8gMSIS\nCy6el+iv8L25Ys+7WGV3EzHWBH6CA2YMWgSjt81yUIJIP7JNZwSU9MoM0eGD\n8sAYsqQ1Jb73oOhjXqdZ+kwZFA5MSMzHENLYVp7RYmy3Og24d2R4vpOHYgOP\nmOs6eXHOiSvdkkQGFNF6P4taaYuIjsNDx+OMfDcN3+Z8GlDZlGKcVsx23/P8\njlI28/Li21nVjOjHX9gjdWodpulZMD+fZqgxPtQOJDxrcenNhWT5180+eePS\nlz2RT/bkFmsZbWY4KodPeFehHeeRl/9TekjuX/iqOwdZUz2PxSTYIJQkI+oU\nVEm5vKEw6adePoyu1eOl0lKUYgye5vcOKZJ7K/D16Iw2lR33oBmb6gp/3sFr\n7jDSi0s8ahCjrizYBcbZYm0HBz2jpFROqaP+icenMDyIDTSyWflWxrubkNAS\nX8aV\r\n=TPtS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCUWOw7XC/lza1MYKGEosN3hGg4Auwd7HCTIZCLwAJbmAIhALOe3eRj9sZ7ZrCePls5zFXMynRRKGAtFsmc9KKQOhHG"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.26_1621937352109_0.9193667008309074"},"_hasShrinkwrap":false},"1.0.1-canary.27":{"name":"plumier","version":"1.0.1-canary.27","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.27+f660372","@plumier/generic-controller":"1.0.1-canary.27+f660372","@types/bytes":"^3.1.0","@types/koa":"^2.13.1","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f660372adfd655e3f70047aaaf92963efd18990c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.27","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-pey61qbJxqyTCQbF37FFq11WwWU0WodN23On0jr451p73mA/GsWY/oqMg3Fm/xQUNjwe9J8i/YUqGQmq/mUK2Q==","shasum":"3a23b5c6cedf77a3dcfcc4f144a76f895a5e3833","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.27.tgz","fileCount":14,"unpackedSize":28866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgsEGyCRA9TVsSAnZWagAAKr4QAIYgiKSet01t2UieBbWm\nbwif8YSbzhzZTb5Usem/bTB1pMgCx07PZ0SNoJF4G3DcVkYaK4t/fisqlwtr\nAmUPuDmxK1qHq+YX3/Oelm6BQoEeepuoHipB7yxQY9PGujH/48YNFIfREc+F\nsccLmtUiZCKuuhOZeBEwd8vZ2Mdc+PJy1UhiPoWQanlPohMBcYmiP19/S+8N\n6jcLj3Z3unAT+Mp20fjguWryP88CXPML+Cn+H8s3vefWsKtc2WfA3veh2Mxw\n3pFDAVBxAeJkOsJtWL5+l/pDt/01j2trFwjCLoD/KLm5BdopJypg7et7H2wJ\n7o0UCfVU/Nh07NVFn5YOKSf7m4Vk5nLvTonVC48bKX0qRiqAh3OdhErT3VM+\nv0dEsTtCJM0ddhGMCzhJdjFbIlDIMAJ77BCU8asWlm8gJnyJHk5npanbdf13\nco8LKI4fG7VA4bxTo6+IHIW0MoFFYBjfqWMrtfznH1sIR5R+dsajVh8PTqgS\nJziZ+PWJ5pqGcN4yh75JexvG8kKMZbaoggTW1nP2hMujLOGbkURl0zlmR89b\nC10so5c/kVhDVcwwVqKYTzFtS11+h0pWZTpzyOK2rYXVNbgtOqPvz/pXgzx8\nhDA3aKtH3SX2aRQf1PY0jOo+vYiObqKzUFZoRDWwcBbReEzvu3Znndt6kqJX\nLgXH\r\n=7htN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDtwCgK9wjRJ9KAOejdXZekYgSUjqBI9AfXNDSxMeC7PQIgV4di2LLW5uAEc6oTDrqVSrhBJ4RdWYHyU5JeE0Er+ck="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.27_1622163890373_0.9897682177666838"},"_hasShrinkwrap":false},"1.0.1-canary.36":{"name":"plumier","version":"1.0.1-canary.36","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.36+b66ea12","@plumier/generic-controller":"1.0.1-canary.36+b66ea12","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b66ea12dccb85cd9e4e681a5860092f9dad71082","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.36","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-qU9bpmxMCALyYumt4YlBGyZwrUTKXWfTEp12smN+v6X2Jp5g/1BFERzelorCc9V+qkD9Vfi1Wwadcjsg4QsXXQ==","shasum":"44977b91a08c1bf48c7b82e3c1bd12ef2d4f7a62","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.36.tgz","fileCount":14,"unpackedSize":28866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguYGWCRA9TVsSAnZWagAAYJEP+wRij+xDKTK+F35PkvZt\nxtQYJ+AICKuXinCkjHjh9ypBYOetj9RSud2cVJiwaY2slCfWJV4O/WjCGS2M\n/LZowylit4U8PeqvWEmfA1AeVuhPlrxKCm5srB3eRLWFYoz+bTncJgl/eE1K\nd/fW+00836Tokx0dn2M4DrguNL2f1Mw+zzFd8v0fAQ9v7p2Pag/acW1Q93Cm\nYTIRrxWEbQKJVKasvJ4LLufJpfJDdffd8L0IZzUfVz9vKlQODuW8WW/RbCnj\nYXaXUltoyw9viI2GPREpw/KtKMkwa7ez4RyWwDya2exSZ6wO8eBEVLuLKU/M\npxFn1/Br9C9ElJ5OLwidAe068Wtyt9AxCsbB9yv+rvAKjQvRJQWCcGPJwGyC\n+UvSe7xs5RuPTcCu7/mqcfo+Ncz0tBh5q6rQqlm8XD8BSnWirchxtcCgPFGP\nkCT4Qv1mRgRB8uEN8jiMk9L+IxsRgYAYMbOcRvK0PSqT5ABq7Cir7emw2Zem\n8sjXN+71yg48vicj5rpyfq1/AXavwlpmOydjTkRUQMUq/PuNEJEMYPS49X5K\n9kOOdAfSbbHvn6QRJofL3GDtRYgSDaySm17pUyK6J2yswNLc/WFWl3dVojTd\n/6SX9hUJETSe9W7LttBTL07BVnDF4A5pDwqZ2vhd12SsMyhO0NVsffpvB5rY\n9A63\r\n=CIpW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID02g+4slTvEJJt0DJPoJKjzRqK4ogGQhSIMtF8zeD2WAiAGHexBjRYwh2svWCZkHG8+AwioQ90LR6xw4vSTvK/A+w=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.36_1622770070024_0.5712528931419074"},"_hasShrinkwrap":false},"1.0.1-canary.40":{"name":"plumier","version":"1.0.1-canary.40","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.1-canary.40+fb4a744","@plumier/generic-controller":"1.0.1-canary.40+fb4a744","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"fb4a74482849071075ff3455a612022b7c9c88a3","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.1-canary.40","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-d9TcJ1YKhHpNmZbfxdl+/avwV9X2HLf63zkHCtR8KZAWbwauf+JkfVea7l/5E+CiZG3aD4LY9RIkOJPamhUiuQ==","shasum":"c70a45fe36f78cb46f1e644eb81a52e3b45017f2","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.1-canary.40.tgz","fileCount":14,"unpackedSize":28866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgvF5ACRA9TVsSAnZWagAABKoQAIqGMtOUWcJcgjGyrZCs\nyxRkLv0E7mOylck7a9Rbm+4p85RNvTZ1HGTkp5LE0c4YDff5mBdC1jz0pix2\nXjUEQcGNbvgrO7A5jTtBab7MvViMXW3rBjN6idzunychvxL4VKqzXWWSu/tD\nPej73n8rGHsAl7926fqRPO3HpgEvwPk19hHNOGk1g6SNaZQnpdTxpUSLGiMe\nVnwFeZJ5b9x9+wT2blKIoyem+c5tE13nw5+i5+5Z3Si0Y7LeUFT+KP54Wes3\n2gBxntisAXe47JjGWsz1H/Ooc2rzeBREDwrFcHhGi/qyktL7dXAdGhwncvVc\nZgpCFFUUeeCFUDLn/siY8HkbBQXwPTWxRXKg0xxRJGa6F4wUbuxZ2tQUZZyu\nxgfAZ2C9owKq+cQs2aR5zdFFmqrcTJtq1wRRmPvajrf97MfJ4NQnBaf7Zp3D\nagSYTkJoWUakBtdTgtv2meV7iAyrZA2wUW68G07pJczHjabIRs50GVd8Tnm+\ngg6RyCnhwV3n3y4FWGxxQpkan0WsMl+Tdj3P1BOfXJTJdSL/C2hDGE5Jifvk\nsqOp0EFsB0m3hEdcYmumKqUmGRE2iMG6zhmjcYnz/U+9G/kAo/ybUhGf0Qfs\nQpJf61Z/7TeKxZSZcRPxYuTr1oES3TxTnMxIS+dRKKBn15G3eEb2Sp25TUgU\nwfDl\r\n=lBgx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC8dn4DxuSg2ugv8sZQXikwpYrE6ATaka+x0SWPIWF0AwIhANiXcRJks13lSoVcuYqo9nrkX8WAdu1oPzqVY/fp1kel"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.1-canary.40_1622957632568_0.73830956401273"},"_hasShrinkwrap":false},"1.0.2":{"name":"plumier","version":"1.0.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"^1.0.2","@plumier/generic-controller":"^1.0.2","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"21a5ddc95244c15fc218146daee6a4e521ce3c0f","_id":"plumier@1.0.2","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-dPppipoLzDjO8MexYIEn2f+dFwmxxlhuCGFCgS2BzDAE/pyy5l8IzCEnoUs+s+4XTE1WBPsdFJIsDkdyl22c5Q==","shasum":"afc58ed80400142468a82014f2e362f8b59e1366","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.2.tgz","fileCount":14,"unpackedSize":29121,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgvS4rCRA9TVsSAnZWagAAVX0P/iBxURdqe26vkq+JkN9p\nTJiqFrIAr6bdM953lFP+YBdHlZW6XcHTmqR1ot7GLUR62wn5yDFl5+WLZNuV\nDiENVSs1v1dFba/Rug7gpdZsn1aNrrQKAhFIoJ/5tUXU59rfAOBv7q+d6Arh\nV6HIS6/dm5nGEdjVlT/fYOIi5XNKiGUx26Gw/xyFoNcYBxYQH2c0kTgm9XIC\nSMe3xU9D7wYIdz8DTCAF12RccJxY6541qw/F/a7pxFOZLoq9LwiEIrlKYkeg\nkNYhuy/dRNMzbfKAZeAfRCrqzIPFV/g1NvU9qNfSA1ey1H+g8+15YGudnZtT\nBR4WIxlJW9/25jdThgP/vQkgkIkkOiJyh54ZQXqIfSkJ38ze5DOUO+VfHPE3\n26LPSoU9FA5tvs3fhc2h4DDjt+JIUQsKKGl+uIgvZrHggjgb8rAjpWzI75fW\n2mnhVotGDmPQ39sJvN2QCcnslOXbRJI7mZsCfuTxmsEcD3W238NblYyzFwMi\nkJ6B7sTi+E3VoMJTAdkhS8WdglEti7dKq0YGEJ6JLlk+9sA4WL9QkdKyL8Q8\nzlPP4n0PdM+TwnvGHVgQgNsxCtsxnyAt2Pct08kbh5vB/b75kXKRdyATlkNL\nmP9EzFbcnjt8MVlK8zdYOsgBsFd07x80kKalE+k7t8/NO3SCpJvvZycZz5mZ\nKuOi\r\n=xlTP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCDHi1skyF7Hl+fsEWFBfyBH+Bl4hlW2l5YDnaRRgAbtQIhAIoVL61TkBrYW6ZOZd//Xhe8yBWHGKt2iQZ2AZDYPcrs"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.2_1623010858935_0.29185745611919534"},"_hasShrinkwrap":false},"1.0.3-canary.3":{"name":"plumier","version":"1.0.3-canary.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.3-canary.3+bac445e","@plumier/generic-controller":"1.0.3-canary.3+bac445e","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bac445e8acee4c72e2c4096f74b6341549d80373","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.3-canary.3","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-mKw/g2J/KEhBV6NGEoXRc07Hv4uRt5uwDxw0Qtx1SlzjLb3gt15TsuO/g6dCfIO64tc+eXRWYXzSGE1LRfnGaA==","shasum":"2495807b097245349d1865cfe64d55534ea1ccb4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.3-canary.3.tgz","fileCount":14,"unpackedSize":29170,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgwgvdCRA9TVsSAnZWagAAKFgP/j/mtHzwZRIfJ6rSEZFT\nKmmcvGt33ejUn9MprHZ0zul4p3oI/vSnlj/BDezbuTG7fA5wZJyi1FkCD10V\nR6Q0g4T9lHreJmioneXjp0lJMY3fxusYl7YBKhZg94Kgwq5ycQeeSco8vie5\nkEoZcPGZLa6aE0tRBufRHMZT7cFzfmHTDubgEdnl9Ecsyvj9enkeI+9NzE6a\n9Gz0QpkElLJEfAoI8BXCemMrO6BPrfUL95yR3wPHKW/kr6rBMHGIWrJEooz1\nnk9TQINkoobtyMqTTrPGlfaBwu2JJGism0n7+ZVQIpnNvrWhnl+on/jXB7we\n1n804JpYiABApDqPIIiTKABTFwMRNQHhg03YjBjfTyry3GnOAWUs/AbXji8y\nuHQhJxCbwYkKOnkWVbTiLTSvkLWd5Z9aU47pC0e2Asom4QbbqBNJmgAvwrcj\nq2+VFaiLvkwjWtMZlFJmFAooftNNm7kUA0IYoZZjgpkwlLYqfQmJgJstOMu5\nIrpR5mrP7RJTY9x9jFLn4semOyLyPZtCb07U6OnGwlmXTmG4mTxHSUgOhkWX\nh6Iyph6/yBUmgKor8pKPhuXAu6TPA8wzZMpJcVYPPbJnMEBp+ihal965DdFA\nHLRmCc3nsT1j9gXlFTV3nAn8LMF7cBvmHO5x+bor6bCmJKCUxc4I1EezyJaZ\nOZbR\r\n=JcaT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGk3LWmwy7l5jHtqPqBtzd4ERUGE/+ZRIWA/elnmhHmQAiEA+Fy26WRQSKWPWjOiLgqNCiD8iXWjxfP90FXwJoNj/0k="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.3-canary.3_1623329757492_0.6136639907631589"},"_hasShrinkwrap":false},"1.0.3-canary.6":{"name":"plumier","version":"1.0.3-canary.6","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.3-canary.6+aca5751","@plumier/generic-controller":"1.0.3-canary.6+aca5751","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"aca5751382bb81bf264df5fbc7dc27df15943874","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.3-canary.6","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-z6K7eAcfMY+JpD3c4C3nlfMyAnz6L6FH0ZYTx/uGn8r66kDNLDD4iyssdjzI2UfmDhSR40LmGUvOZfenoBU+wQ==","shasum":"628bd9d698a884221635123b31452998e0e8c6a4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.3-canary.6.tgz","fileCount":14,"unpackedSize":29170,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgxTT1CRA9TVsSAnZWagAAglsP/0ETnx8jx0JT79xxjsOg\nnEG8MMsTIwAg87dF9Ic3137a2tIIwd3Qe3djF0Y67J1SjxDUr4LDg3KnQO3O\nDdKLSCIf6Ohxj9KCvUXlSWa3v9M79szVOYgx1ttAGHKsvT0axZ1R25+UdQcc\nBKCs9y/lKFHdDnRfu9DoQC+qZ6VGwu3ZEbFn1pQQKRwfW8WCKXQwgKJ2u2EN\n7JoNj7GI32jlDjfK9xyaUruJMOqhKGO/Bb/nG9fd2OvtUjUem9zHiATkksVu\ng5ye3F0dBFvUd1YenAqhpkCFBapWNO30HE2/2ntm0IbM9IXTpf3f/C2ruxjy\niE+Hjzjhw3xHFW/VMVYq9u/KLez8Nqi/Ix7sCUA20mHSKxNFCtu6ALayjx0I\npXjubIDCFLMTxC35226cpQYY7J4i/gL8KOwpYQ9RDHISmPmIaa46JKXRNUbG\nGfvzon3ouEkmVv2nmeHCNGyA4trL2XF/Yf+PQP1j0ThppT86byrmxr8a1Up0\n7HvkrbRKmvZynvghoygjJJwORqTw7QTAN9UTbsSg4x9LsmLKtsXCJZJL+WMr\njk1iQOAcwL+oODfyEQG2NlvNTSuq5JFzFlUYGvvz1xWXyK7DZPuiNepY9nQN\naJui24BT8M0vRqTaQdI5ikr3UtiVvPPpV3D6skMpLnFp0SvcNVyh25hnqGb7\nboit\r\n=biAN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHAh7pCv5xAKF22Ws+JF+iQQP12pGBhkPcdOVVkW+xFyAiByOTXGUmdYE427Z92h+Rf3+RwrDaCixUyKzDMW11fMQg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.3-canary.6_1623536885656_0.007698607463091589"},"_hasShrinkwrap":false},"1.0.3-canary.7":{"name":"plumier","version":"1.0.3-canary.7","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.3-canary.7+c06c017","@plumier/generic-controller":"1.0.3-canary.7+c06c017","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c06c01756a7c3d54919bf2ceb913d739b1a1841c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.3-canary.7","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-82cWm4zGKeWDTl0vhU6Pt3+ai4DpuNv/W/oMsWVnhTLRJUFpjmDXevzUhv8r8zHmOMzM+4ybC14VOxEPeIA9zA==","shasum":"1cc23beee3c4c86ee2ea6745ff887b2886db4319","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.3-canary.7.tgz","fileCount":14,"unpackedSize":29170,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgx8YxCRA9TVsSAnZWagAAa6MP/Ajipxkf5AlgwJGbr+/1\nlqPrGtlKRvjWBQQbHBUGHEYV1UOECmOYel9oRN9lhhiulL3tugXEb9RS+jYg\nmjobxWvIkER2RmEWwWyEvy4nCrny5h++Dw1ItE/sGpV6ccbqu+MmivZzH3ms\nStiVhJfC85eK497ZiBiE6cRLBxJrESxYn6dAiP1B2eiNyRdLPgiaAOw4DQRs\nWL5WnXKSJ/40RjndkzXtE8jzU5CgA8m5c1g1xQuQ3A//D0ZqihSL4ahl3z5c\nyH3tJjlyYrksOWB3f9UDS21Qd3Is4zR9zWsmepHgXozQ9APpi51auzponJmY\nfBiaKPCnel9EuZ4DSSPBq8dZjJuP4gv+kDC+wCoXrBFW0eztMbJvMP/MQwxk\nFo1e3/gpxV1yy6X4ESkAHqsaWABtGVrgM1zM5/3ruGriaTuu03hMbI6VMaCd\nWbLS6X3KGMFgedyHTCpBjP0U/VJfTvfq99Go9dBdbUnKpv5qNAVkxCofHCA/\nKiVXtSd9gK6/vXRJccarA1UyemAaYGeGlqxQIpqNjG4FJE59i/bw7l/04mkS\nAAW0E+pZJ2QS0NmDKfg8mJ1z/8xif/AEJy9jwGniwzqYv80KCwabeB4z5V/I\nQnxE9YUmtRi3/LhLvRL/dEfIuNVwKGZG/gK5M0/K42hU2RXoGbV/9PzrTY0v\n2Qt4\r\n=xq+J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDcO6RbAcEmqHAy6nfnKPHn+FqREo+GhT2/TP/3zQzmcgIhAIHYPATRrfcXRV6/HeVyRaj9W6YW51QkKR0Y/l6H9KcC"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.3-canary.7_1623705137303_0.03993557563983452"},"_hasShrinkwrap":false},"1.0.3-canary.9":{"name":"plumier","version":"1.0.3-canary.9","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.3-canary.9+bcd544b","@plumier/generic-controller":"1.0.3-canary.9+bcd544b","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"bcd544b68e2c6a6a9f5c9e33a54f8098b4b06d65","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.3-canary.9","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-kPIiy8w985jPBdGQbYAmHWyRxsozY1xQPMpjKJy72usfysSNcbbhQAdbNDQyz1vPlU9jskFPPqTjWQjwyvWRUA==","shasum":"1a3153059e2f3cee5ce911271fc4efb6037a6784","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.3-canary.9.tgz","fileCount":14,"unpackedSize":29170,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgx93GCRA9TVsSAnZWagAArDQP/2kd9jPWWTK0FF9hzneg\niKRMKfkunT6dWFS+D3t98zhUXDrIDyR6AfRTQTea3OMhWb4VlX5jLjPzblix\nLn3Z860nP6LS6AppO9N30GDhfFTiJi46n3DHROaERN/iCdXJVDevsz2a2g9n\nOI6Mn9n2GiTKbfmeNLyxrDIw3JCKptlr1tnCYENpmYtUhwd1MUqI/BcItNwR\nmopXLbMMXqepAqJoFkDZXR+2zmhSZep9FaDirm2JB5CF7zYUUsMKuCgSEOep\n3sF0WD1r3Ylws4llUil6x5bAEoBce0LgrcucA80TaEfDMuEHlw2DT5PjgNcH\n8mpZgoiL/FW63OEQjpfebSnyJu7zlL8ysYffRhBF+jeUtcsnmb7npY/YEqZi\ngWLfRaFpyfPwUju06g/jDR5gR4NWi2n96YpML5xarpd4GYfItpLrPB24NmFD\ne1xtPzztejumu+XDVZzLXADkGIFHjiVm/gNIF+oEY/QxWE5QEROyUU9HpWrS\nQHBg6jw+uEYbAqtjAgm1NPrfj2J3mRnHVT/NGmdufIcd94hS7gwQQeVOtLy/\nVmgOZS80CppvXxxcxkwakE2IHcR1akwqFRybT7PmT1+4ElfS5+dC1/Rts3wh\nCPpNZqXgXsyuSLOUv6KcEfdAtRpdtJ6GXrMBjj8zuOMdh60ulkb2mvmFXBhe\ncgxF\r\n=kb29\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGtCW7B+VgWRX3xNV4Zonnr1SmSSzwhSTTXj5St6oO93AiEA5PPPhX+oiXA2rLOAE2SQl8EOEaZqgNqQ3OYTwM49PcE="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.3-canary.9_1623711174677_0.36758707260676293"},"_hasShrinkwrap":false},"1.0.3-canary.10":{"name":"plumier","version":"1.0.3-canary.10","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.3-canary.10+af94bc7","@plumier/generic-controller":"1.0.3-canary.10+af94bc7","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"af94bc7d38c999dfd3c1509b34185e3384eed858","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.3-canary.10","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-aHASMoGE5cm4mUuxI/e3PBqVbSbK2sBm00o2Sspcj7k1e3/j4IBwiwKvgtljrpxtBMho4VZrPN2S2KF5dEXlOg==","shasum":"7c3167afb7a75579fda4553178f0701b090db3b3","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.3-canary.10.tgz","fileCount":14,"unpackedSize":29173,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgyqw+CRA9TVsSAnZWagAA8N0P/1Dwb5Nc11wDJTvC8cte\ndL2jhrLh6UG34KpA9jvTqQdhrYFsQfrx+97vWkhXF56IxVAeOO5O1a6Q+8Cr\nP/m/iyerHo1nyeOi3riM8383g1NmwD9N3FGPtwbfNOtynTZm/1aSNgz/nxxU\nSFl+PaUfNzc2rBekflOb3RLl1dEKSfuiwOe2dRU3W2sUA+w0urPO4NiIMhNm\nkTqo1cUZRsreYAB/qw5zAiaCx+DRwqF0q1/6DOwg9ivkSzepRCQXles51WV3\nkf4OifdSoy6WzlVG2IcFkuV+PqI1jgeclNZt8XWNDQc86AgMgJL5pPo3vQt/\nt8nlO5h1wHoWEkjXaU1S4Utp7u137KLkE+HZGuuXX0EUGWlPFaxCt6FuomrL\nmr+TPXNtA+mLTDpcYL/QHWaL/bbZJuYnkNC2T090DC1lfZ6D8vEJqWnhrvwf\nYuKvfzBMM1mwEMbnMWKQw1jAcCTAl0sPDa8b0edIdo4eX2KYHffouWfOeAAG\npXpHWFTtY3nj2b6GSRO5Ou/zdaz3YhIylXilOrRjxVv5qEKCZwvuofJKIvg4\nc7s3o2P73Aag8Jd7r27pBLgJrNLJPEtjSnYCJt4SQphRfTbi2hCL/ksHI/D3\n8LHxCJjnYr+4qmOm14GUHTRdLtWlqOpHHMvH6NQL3w0b+mVBoCRcGwHTk93o\nxvlY\r\n=xRgb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCcrRmMPTNleb1pdjPBz32z5PNnjG+XH/lHCGi3nPFNiwIgJOC4NlMCCG1Ou9wwFSdK3Nr7N88xK7Zij/cMRlPY/jo="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.3-canary.10_1623895101670_0.44078527534217105"},"_hasShrinkwrap":false},"1.0.3-canary.13":{"name":"plumier","version":"1.0.3-canary.13","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.3-canary.13+e1d725a","@plumier/generic-controller":"1.0.3-canary.13+e1d725a","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e1d725a20031673de8774b6555ef61a5178c44be","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.3-canary.13","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-BGWqQLxagZIvE9WMZiEQFa6iPpx8lVQnqHbXIrmTIZNgnXOUKfr7Zdhc29LQQNEYAE8fodJXqwJiuZmqKrchUQ==","shasum":"0a4e5925a3bdd3906c1d43eadd8b4c03859a4581","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.3-canary.13.tgz","fileCount":14,"unpackedSize":29173,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0s6wCRA9TVsSAnZWagAAcNoP/1CqVwZvqmhkoc8HB81u\nU+CW2TxDzQbVQySGtQtrE9jMiiMD8fsCyaPJXuNnR3gZUpGS/u9gYTi/5bTB\niGOvheZwheN8xR3h9BrgJtnF/MuHaTWiYkqxxAHAU/83xdsWfZisbTVpdT6X\nVbKy5uwm6DHWcAPfROCFjuC/LYurCbrFMHa3PtnIOW+kqIewoBYjeCu+MtAF\niwAHuUNzUeOztLiZec3zIj98yw4YoOUJdYeDoC7fowSbwKjzhP/rfJAuvaS3\n6MT7KRqO57YESQymmDlQn/3PXPCLFxJOI33elMrsDXqv4gnZ1VioYp2st+Af\nsjWXZL6s+ot3QbgxiEocDs4SqrJKH9FoPVBMCoH3EZ+1XREPsdwSI/tiZee/\nIaYS2cwbN/o3UWI5gw+gEheE5u4xwjkPZWocqy06Q0vVlhW4moMmyImCWX+V\nEgJw3EZlMRboVFl8q1XKraxGrBSOFGF7PqCC4hQjzf5tvXQ3W91LVw5mIu9G\nYgr+B0b8XtCbWzQXLZ9YKZNkt6m5h65aMe+NHuX8EqKj+RY1KUDbJKKR+7PL\ntAHE+GXm8UEyphTn4hgR8rlsJmOb8GaQQVITUeXfFTUWvJRLag6QE52iiY9l\nEBZeiPTAXMneRI/dgXO6vQgwfcWZXgEKCOabSLBdDqW5A0Svx6i+OXIhkSFS\nF5Cr\r\n=kWRS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCEOFQuYEyhtj0HMv25VtIDlmhAYKVqU57thFU0f01rfAIge1ypLGjF4dQNWeZPeYP2+0gDvjIZ47i6tbawZLgjMP0="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.3-canary.13_1624428208385_0.2147720908819668"},"_hasShrinkwrap":false},"1.0.3-canary.16":{"name":"plumier","version":"1.0.3-canary.16","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.3-canary.16+6a18218","@plumier/generic-controller":"1.0.3-canary.16+6a18218","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"6a182186de06555adcb4d7864d177ee8a4df9413","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.3-canary.16","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-2JkFDlq625Enli4psQqUpcemilXXNPcVLFeVxK8mShgsPBYhjC7AgRusAb2oJNQqzfLHHRnjYHEqaksp2wb3eA==","shasum":"dfaab283f7f8c02b47c65fe74c61246f2e5416cd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.3-canary.16.tgz","fileCount":14,"unpackedSize":29173,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg2WvyCRA9TVsSAnZWagAA8msP/iNmzqtE7+8FYvryRBi4\nWmHMlVZytPMKhpGiWal5z/MNbtFgfYEWt0ZSSxvi6ysSw/Tkv/3563VK14zx\nA8kS6O4yXA+QkCNdQIhDya5IxOAfoq6A2xOvKj2QPB9uMC6X1PmO9vPO+NuF\nZAOBAUB0+SSM9tDzV1FeLiYABnXf8iLWrjyQIR29Q0kEnaHW68rgeAYDWoZY\nC6RBTmx3oErzlI8EczjK+5BLBr/64zIlmohtBB66poqgharlSpbdDulxkcmj\nLkKc9u6Fqm7z+CJyCV9MxDnzC2Oncy2hoBPBpad0WFLBWzZrL6y4L9jW1koz\nUNLnoaxpOU/r12Y4cTnd+QTkdPimV5E8mUtfGH8uzVb/OKDEO9rfyIF4FzTR\nf2AyTypEBsweomfvCksyZuQEeQCUGs2JfvnD5t8OrIkxagdf6/da8bMVFZuo\nup/RokXpsd8yJhnBR1MGiZiBQXe+sMz1IKOliVu7oKUWM4altjKDAPspFieE\nvYpbIOraJwP3oxYQhIjDAcHz4DWvjz+FqoxafgFqH/KLIZKtNsyQH4fyMBzX\nfzpukSZ+SjI3NKz0QFxdAzn5Eq18SSeXg0ee8L6pVwo43yVC12GH897JU8J3\n4n1T4rVFDzokIZ1Wt5Edt8qQrNd6mMhIcaXAQc39iC0dKh/vZbb6FkN2AtFH\nkRVS\r\n=NFax\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCxusenb4ehEtOpifhZ12MP/sViCeElJnQV3UEKoH3bmwIhAP8jApkvgfMwsmXLX4L5iE/Zt+f9huJsb4hy+08C0fM4"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.3-canary.16_1624861681953_0.13066236594017488"},"_hasShrinkwrap":false},"1.0.3":{"name":"plumier","version":"1.0.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"^1.0.3","@plumier/generic-controller":"^1.0.3","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"31e9b4a51607ec43050585daf1bde362842334dd","_id":"plumier@1.0.3","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-5zu/VHpPLn1CBEIMsexKoP2u7uP0Ev7CbxCN/WASYwUQ8gLhg+KgXPDF1O65mYIvXnSEAiwIrru9/6KJQ/Gtew==","shasum":"83b16d873aa431b5cc8f53426e3b3275232ee5b5","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.3.tgz","fileCount":14,"unpackedSize":29259,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg2YNaCRA9TVsSAnZWagAAeQoQAKEJRE3I5aaU9WH4cpMA\nQS0wxJjHIgoHdlZPYeEtSWvqgj3g4TlYfM8HS86DBiFW1HN+0GFRW0Brzk7T\nD/UQSTlzdpfhvI861rY+gE4/GfRl5vbSnP05NzikpU27c7yE5qmsFc9mdX0e\ngPYlURdPtfuJ03k4ZEpjkxHOGmngV8MJbLCAiY58Hzy0VW3cD97GI0INWj3w\nWzCerwDejYIQHsbRAJoLPJ7vcwfmSOXgFHYzYdW+6tf9QUIqtQT1sVNCgrhQ\n2+uB37C1j7Im1CPK5+eV+Nka7VJhnABrNu4F3fDdBGE4KbtFrtok1nPwHOMm\nsuOr6wRQREGCPmqX3bRvtxmG6CpszcSKNBcOdbAxlWHWrv/7Wv/K4o1UF6Ab\nBMw/jkyY0uZIiP5T35e3AaT+YrjMTuP15dYJjklAUfkkD7gNOabWyD2TC2WQ\n6eosT97aIRP1r4SBuFXcnXCXcnKGauXB9fjRr/FbHNR/QBav1cCXwCv9aqh1\nzrG07P5VLm/9CNheBKPYUn8UnSth+/jQlcj/xGaWC+Dy7o19UAMzqQoekn2b\nmjLhxATju/fO1yhyP5fAHCUBliVy0X7knim0Uq3uCV/Liqby2rd/iv/g9kfo\nEoQZMiuW/0aU16y0yNhPXqhXiQuwnmA8djsHqwfSiCKohoceH1UDlRMgspPi\nB7LT\r\n=g31a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCdwXY5mLtvrndY+JrZpj6PA5i2wtmpoDKwG7fcu86EaAIhAJCacIsjZLPKy347XYYoBhymNpu6AJz3RkFPbvpwYVxD"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.3_1624867673670_0.014169715962956353"},"_hasShrinkwrap":false},"1.0.4-canary.1":{"name":"plumier","version":"1.0.4-canary.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.4-canary.1+dc54af9","@plumier/generic-controller":"1.0.4-canary.1+dc54af9","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"dc54af982b53f0a1a6119b3ca34928a73b773eac","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.4-canary.1","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-tMb/WmvV/dxLev2bpdlWyH4h86FEXn10j5/W04nZxQyBH81xJuHV6UsFpXYa7XK7ePqQVnR9FJ2pWCn7F/7Z4w==","shasum":"416f9695bdbe8778ab5b35a79d60fb19e979bf2f","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.4-canary.1.tgz","fileCount":14,"unpackedSize":29308,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3kb5CRA9TVsSAnZWagAAf2cP/jqaQ7GHk5dzc7PFepO+\nvcolQPJV0l70XytKsuYye9aT/1VCryrxlTlgvDPaDbkO8GomOfEJ/KKfkN34\nwaWcZxQ3QBTof2vcmjwKK4vkOWORkuhh0p1FO48nJmeFnsqsB/NW7e3XZGjK\ny6O7icgoa7PipF3IZbW2cTwomaCP20zsQdcKTo5nyJZXfVTXmyT6VGxn9f0F\nxdTw/Vnx2lmAliykX1RqUdFs2EXTUn7Rbes7PGEk4kv3Cyvgf9dd4V/ODmVc\nb+oz0oBIAyCRv99ui5iKUT/slVgyv6xnybEw2u2iYyYkjN9btH2tHLFPa0LQ\nCB6z6VTVOFgQwhEiMAaF029QUxMw2fhrskteInG4XAOet+ccgTqjo8EsLc1F\njYGZYQwnp7idoxSyHxBO3uX6a5ZZk6stkE73JUZxKeGqxriFpTiVS/XYJKwZ\n1UPRsYdORhaq9kcxpHddQpyYh731h8Rk2qsjjwXSmzqYZENT/XUHM8zHbd18\neWxjHtWcJznqkuOEBNwyp+Blq5daVGPoj/88hgBKsG1/By52ekE5IuLj8jrh\npJY0z/Hw3t888PXiDDQTUO7VTEmD30sqVCTLfKbk1cfctnQza6xka4fgOhGX\n8kCboaNv12/ZTeTLTQUUNfqSh/sJ7DzEKEXAjok13JE+VAg9nMdRzyBjjWlU\niCbS\r\n=15E+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDFs+HnzgokB6S61TEPtTx09FS4YNB9OBBlpEXqPpS4wQIgaBOyO8m5F7Q6hdMj334hnTeM01wexx8EBjpM/hwy9E0="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.4-canary.1_1625179897399_0.9688473473688182"},"_hasShrinkwrap":false},"1.0.4-canary.2":{"name":"plumier","version":"1.0.4-canary.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.4-canary.2+65d1cfd","@plumier/generic-controller":"1.0.4-canary.2+65d1cfd","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"65d1cfdbb3b46d42c312b7b48ede7a822ca53437","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.4-canary.2","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-5r9mhYouW6AwFKDJfPJvERUfbw3DnNVczRESt620PfzQiu+nPSH/p0io84gdRBgEu2h+AIJ6LDHZHWNlK1ft6Q==","shasum":"c2f0010f6b6c92603f9d2f2845bda50e33b36341","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.4-canary.2.tgz","fileCount":14,"unpackedSize":29308,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3qBFCRA9TVsSAnZWagAA2dsP/A6u771QccLVI2x4Jle5\n5Yt6j9d3EK8rN/HjgGV1HrEnmU8DzZQlYwOz0vR//gythuq/BGBlwJRWKl2+\nkTDGpZ8BUhBcyhYH8tPcQHjjZVLrMgmNrzLKsQgdxoaY4Q+O4ncIG6Os7giD\ndWWdNJt05rSo3ouGVI3OHfLNBEf1zyrcOobIG3nFwgY8/Egm47fVYi3QTTUy\npb6rMf23Iz31Q6VsRm7zEusEx+ciicKN5ATqDbqs7Q2ARXsxtNnFy8KrRgxS\nseeYSEIsjM2DOyeHOnXaOpErXpY63TrdMU83pr/vNnbSuR5BkZpMZ3qSG/82\n3fqIc9V2iiPQHhscpTUCNeLzz87VHs+kzrYdF7U+zM5/Da67y8/ArKDS6qBt\nqliqFu681nP5G3gazDMSAJH84IIzlDVRrsJYtZCZc4TMVSIYx15lRmKL2JI6\nmC7OllKFUI6poiHV5wKlHZiTHF3ZFT2Qzbyt829qXrnfaIT+TmBi2l0h5LLh\nH/KMle8ogLnIii46V0A1FWneWsVSoDA6RnWxpP/uuB3C9btbQss5Sv4g0x6j\nOejnzOVTZQGXu2kLswMCsQXe2CqXIvxyg64wwTOHqGI6q8S1S2iT2/8zA7fG\nTokY4egPL7Ahrn1z++JAb8IO9s0qTnPdbdDdo9o/5ShDDyumasea0/As2tEp\n5Cib\r\n=7gm1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCNfxgRJVDGP6wiSBKLVFss+wOZf5ZuREX00U19aoauRgIhAMo3DOQzioajYt/frQlHo6UjhLJlKATqWnqcUSv/FtF2"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.4-canary.2_1625202756715_0.6520533389465231"},"_hasShrinkwrap":false},"1.0.4-canary.5":{"name":"plumier","version":"1.0.4-canary.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.4-canary.5+5d50984","@plumier/generic-controller":"1.0.4-canary.5+5d50984","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5d50984c35979bbaa12d52327e3783b459cb849b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.4-canary.5","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-saRUV6CrzOVoH0mjH+PcrDp+wL1ocMvkkun5NHFovmC/aVTJhEWj8U/bIC1zMkhS4zLOVXqjvAac+4YShpYTyQ==","shasum":"cca3887bc2cf18eb8d2a59af53a36f2ffa65ed71","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.4-canary.5.tgz","fileCount":14,"unpackedSize":29308,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg4Bv/CRA9TVsSAnZWagAAmk8P+gNB+FlVrRKtucDTkiqK\nJvGZRgGCIc8s6k6cIDDMBlkfkS6xvvlwQS8xSC/CAuzdYH4ZScDz2+0CnHP8\nkfeFPdyF+4iTOw+FZS6EGLOXZq/5xxcUVYOq1G/9M11ZtpPRJHcVTIZo/p06\n6eZnauVbHYW5Tkbkfqo9K/XM/thACoCSOYTTxBZR9unqhQJYfyOnI83pi/jD\nUdZe6bhAWazcrueUFqR6c4bGmQiByHdzDEi0FTQO7y/df2yIQJ7jVLAbzqX+\nrM/eVnTuNY5YUxSWsqhCv51zgi5vJ+vEuetX3DCJwZmctc+bhNSdDo20SI8y\nZJVODDc9Fi+lh5Fh9+oDdL1ByqICVNV979qQ3JrNqEPGBWPYEZ9yBq0lUFKo\nwgFfG22x8UBMt1Stm2QEKDst352rEPKkTgD87a+Ax3NJ+xb0d+JjTDcr7R1B\nxOd98RuyAImFXOt7viU0s644KRpT41vEMyQFUMEWxbEAugJNXG3to3EKwGZs\n+UBnTa3/ci707Ej/0VQlQLdFE/renxJXoGRYjQ1HkDMT+7hshJ6tdOIbGBt8\nKGf0E44ddndyXR+1dSN9GF4NwL80HnKkNKp27Nvx3oSwAaKUQBRPJdshLDfo\nmMNPw70PkW6dGNpZ60mxY2Nx5KFBYn4R7t0SxAI6dcALlu1h54ZRGkRXSkca\nAQBX\r\n=pcRO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFM5R+sWe9nCQ/DP2cOn0BetgRnOMNtiPHHue5gHEse/AiBBhJ5EUffCIZaAm2Yijlx6LpJijNbRbz1xmaZSDCH4OA=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.4-canary.5_1625299966604_0.8076930173549115"},"_hasShrinkwrap":false},"1.0.4":{"name":"plumier","version":"1.0.4","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"^1.0.3","@plumier/generic-controller":"^1.0.4","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"15f9ee50908f37b5099d74119eed7eb1eef4cb9b","_id":"plumier@1.0.4","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-g5rsAsKeRHXelhg6wsL2vIYFwBcKeAYbkcRUTmpKB7YloCrlrXZ72HfxAsBPdEd2y1cYGQFiloObw4PAdj5Lag==","shasum":"68b23727d25d7f9705b7a039b8d66dd3bed2ed59","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.4.tgz","fileCount":14,"unpackedSize":29397,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg40bZCRA9TVsSAnZWagAA204P/A6dkycEGfB4kQzNZ1My\nEybkKGm/wuWKgFRPr1785VLfF7Cm3RlmnQvIzzSR18oKH1NSv0nh6LQ0c+q8\nAUOCF7YD/7YoAV09nFPs/eoJ3AegJRvvd20U0cn0VKc7EqeVUt+AqRl7CZTo\nuSssVJNT6mEdaBzV2zfkkCh4lHmeyMuYOtWXKOtc73sB7xrP5os9K7/zIa2o\nOFfrZIobSg5JiGnguqwLA3WhXbE9dohQcGF6Ji0yH2qkOOPWeCHlF/yAgaiF\nh7V/oBVmy+BQjh5glLQab4POkDcnz8VrzsBQ6K2iHP2WJnzNaUDUW6caeTKt\nit3b6GwReJwFbm2PBvgdgMzagwhYyA2RAXEzxoeSxRRfzAGFYPnwG/4l3c9r\nuP5hnO8Q9alhDnjHIWA/5i23vmyi3Mel9OmHXOfHzXsv/MTvf8CW0Og9Tjuy\nEZFui6HClN45v7oLN3k2yzGunWsa4rTqmNAV4XD7UDLwaUJ6PIZevD0xjuGP\n6nS70kZsEj+D2U6G+1Xj4ZmMOT0v31DOZDP91KPrsyBXEM26lus5oBZyXZA5\nF7et3F/y+OwvZPv47zlzAwrPtBSw7GvFi/IDDjhRy/GbSFdGuv8Spvh7jCxv\n/fFajjk0cKeZ/Gqb4ukuCEHI7j8NWOnZ3FwNZOvscNJvEiV1B3M4ORN+K0rL\nRviZ\r\n=PPae\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBd1UuuzHbKlp55tuV5n7g8mXj/3+UYnTLtrj+TdX+MKAiEAyFOAFEIM2QseA5YTfA6/eAjsu+V+6S5wPZ+7qqnRDo4="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.4_1625507545256_0.6595630397834797"},"_hasShrinkwrap":false},"1.0.5-canary.1":{"name":"plumier","version":"1.0.5-canary.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.5-canary.1+0c50e91","@plumier/generic-controller":"1.0.5-canary.1+0c50e91","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0c50e91a91889604accedd79f13534f25f41d98c","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.5-canary.1","_nodeVersion":"12.22.1","_npmVersion":"lerna/4.0.0/node@v12.22.1+x64 (linux)","dist":{"integrity":"sha512-WT/VIALjsI+yxEhlgJTrzp6rx8L0AZtoUpcf8wsNAPNTl6eNERuiMVytel4nF51uQkQGJ/K9dSNVHYJPQH1iuQ==","shasum":"1c53a964783028538ad79c713ec3bb61cf48fe49","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.5-canary.1.tgz","fileCount":14,"unpackedSize":29446,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg55sqCRA9TVsSAnZWagAA340P/3k9AgHEGcylw33Bu3KC\ne79LlmhBagy938Emj79BZVVe0tnB+Jm7LO9jv/EUTfrnvJKsMl7jOrBw/P7k\niiuwU8X9mZQiw8wS8qklJi4vYtqcNIe2fX6OflDexP/po+MGalBUuo/I4RQ1\nVj/aFLumGNx55Cq81y+HY365JiUelazM4V1HvKW3rxZaJab5EZs6TzddPQcu\ngZd/nDlirbNh9nZSH7YMi3h4neXVk158ROHyWFadeynTULeZc/WIjR/WX6Cw\nQYSWXngrnA4zlMAxybIo0RsCRHnodCFXmbGp+ni74e4S3vr7KmVcJA/IkpO4\nUSi3vWoDe8VUt/nf0JUxiS7fz3paiUS6rog4g6afBloHvRV/nixSESdxh70O\nmU7ttbMS29mYVL8/tVpw9iW76b7qdTY7W6uYN1Ov+883Fuf+cmG/SzWpX3kg\nWv7N3Z4t3CzB7ipABuBSuPmTLGRP8ch0gvkPAMR4AtaYQ915pzM6yb2uBt6Y\n7ZThinshaTZpjXS7umva6xxE6MeCxSvlcvsZFyymjJJxPfnni9dVFajqySRm\nYdkWnJNVZZeWmhtAhnRE8axBLCJspswW8jmPZU2EVlhzrKtbxGbnGm+wQRWD\nM/ogCATPw0tdatki8hJ4WuzdEnjp5X/DEV1OGt9Z1LhfYCQ4SiJ3kq3FJ6EZ\n1oj2\r\n=mhsa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFV+p61Wac/ikgsvxLCpB7vBUqrkL6FUw5480NlhwRm2AiBADovSuxLzvUJkBm1/lcVTWXUcdrnTWRQ/xVAQIJuedQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.5-canary.1_1625791274383_0.6229019887002334"},"_hasShrinkwrap":false},"1.0.5-canary.3":{"name":"plumier","version":"1.0.5-canary.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.5-canary.3+f153bed","@plumier/generic-controller":"1.0.5-canary.3+f153bed","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f153bedd6bb1c960316c2e233f055d8e4eae0502","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.5-canary.3","_nodeVersion":"12.22.3","_npmVersion":"lerna/4.0.0/node@v12.22.3+x64 (linux)","dist":{"integrity":"sha512-IwnqwRijh/W/Bs5ROcyZoM0Pavxfl1ZI1U/ITgiYOOET6cwW+ahjdXX94SENRLN6mUmCTUHN/gnL/Zw4MGELrA==","shasum":"a5a8cdbe5130e53a225e56a87c2c29af8db99b84","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.5-canary.3.tgz","fileCount":14,"unpackedSize":29446,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+g/xCRA9TVsSAnZWagAABkEP/iut+cpUB6EZrMsidZQ2\nJv6FjSueyPwYMMl3EINMjZlz0pEAECg2+L5Xap7xU8Y+773a2caDa+vjv0IO\nahkNk7WayCtzKzJwqx6YAadAvUGiMFILout6EvEYU0uoorFEk1m25+KGvBI5\nlBqjA7UI3GxFDmSM2lvN1/dcUUCRZGlcA9Za1wKzoOE/iLSPUNC8hAPd7nKo\njeEZsriv24ZT3LVvuwt8IpfE6wcmqFXrS4zsgCiWBJ2eqGFMIvTg3FBtms/r\n5TDYiUHoR4WMN3CIepobji3RRjzJmTT5zcJLhAr1BJB9caL9pQR/GdmcntgT\nNe3/Sh1HlXq8nt5+XCJyLB1d7GmNzekrwIhxtjuoqKus4K0pSaPh5y7pSHOl\nXhCv4gEWW6zs/cqxdKHNGldbh8XgNebLBfO1kGKoEeBtymX5NOtBSJajwBbQ\nyk9Se3NV92nYEnfJFyJWj/A0DND8rURUW9hFdHIEq+Rpd5hoy2j6zUooaldm\n4SQ1QTFmuqFlfPoToYPYo/s/ND7XPSwHAXJyHD9ykw8s2JWK82Hz3JJktyTQ\npppMuMyCh+BPUhtftt9b9z1d3aAMH/C6fVqB7zFZAakb2sLxQY8of+sb3WvE\nFmHv/4QW5fpblfHR/e55qIvi2vF7NCef5TX8F5cQvo9TGMl6p5mokqNnoeMR\n5Hey\r\n=sVbg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwPJ8zmQGbqmdN4Xh08IQY1F0IueoatymfDtULSxEjigIhAIdzZVzElhubG1cWS5gxCUPD8gg4YkqnFko71Qf7qt5r"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.5-canary.3_1627000817675_0.4948915049992273"},"_hasShrinkwrap":false},"1.0.5":{"name":"plumier","version":"1.0.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"^1.0.5","@plumier/generic-controller":"^1.0.5","@types/bytes":"^3.1.0","@types/koa":"^2.13.3","@types/koa__cors":"^3.0.2","@types/qs":"^6.9.6","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"5f6d204483657909bac31851522176b2b500e583","_id":"plumier@1.0.5","_nodeVersion":"12.22.3","_npmVersion":"lerna/4.0.0/node@v12.22.3+x64 (linux)","dist":{"integrity":"sha512-2bRUPMbzkQNkcMlpveMlCEZyUVU15gd0yP4/NMTjtakWgf6RXc/F13sYdYZo/SSWxvjxUyH3l7Xp4Gf825G8Ww==","shasum":"f5a8f837a0f4839e3a3d6527490dd64c9f63d5d0","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.5.tgz","fileCount":14,"unpackedSize":29535,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg/OlGCRA9TVsSAnZWagAA0DAQAIIbZV9a6qW6uATiMuWp\nOUP0NvVIAi8+KtRpIQKqB8vOQd62EJ8Xi6GePyg4wJMfzvobzLLuqbvbI4e2\n87QAwY9zY9YJY5Oekb8uzK0NcGg8glylhF/PAv+JHn+qwVPk7z6IVRyDGypW\nbDQaAwca6pnxaTyszu1iT52j74T9B4aP/iLUNhnBAVhMYP7C5yGKzTJRvh+h\n1d3/7Ymmdhx6CAqt8ikHKlEcsWEguw4SJdP02Q/RyYYrDrUWNRksUx5pT75F\nLUJcHLLq5RV/2HJO9wyTzvjaUCIeWnvDm17ff/qALUjhm1W9CpkElQgW6RGT\nruSa5jjpzoos8+ti7LquTXAH3LENWRjxZqhzWwXQ3uT0GnG+G8mNiK5uYDOH\ng95CVTpW6sRQUiEjoI5cxqExRJQ//K/a87KVO70R95DQN2d/sUozutb+uuuN\nZTURHvxHuWIwl76BaJ9XwlK+x1Ei3CNkKFrWYVeuT8MziDUBGx8Lro+uX16i\nPsZTb1AyFvPT4ma8xSK9D0w6jWbxR7bVvo1ykBbOHZOPBH28i/ZE4JhI3Qof\n4tkw6NlI7EdkHLYFI2IVjPuVVAg0qh76Sw/8py+oFLqa3PfqP8zF74QT8XOe\n0/I7bEVEdwSMujCrFWqFgpn54b289XaobwYy5vTxRoBKnULV/HArAP1PnhYA\nZtEB\r\n=Az1v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC0e9c+rkqf0r4/+EQCTkMlISayeoQOTdTqulB57uohYgIhAI4MQvS94xia1ZZ/sRPo+3/1I5v90Ul6VNgKHEy86HWJ"}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.5_1627187526055_0.07446175172365899"},"_hasShrinkwrap":false},"1.0.6-canary.3":{"name":"plumier","version":"1.0.6-canary.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.6-canary.3+b235819","@plumier/generic-controller":"1.0.6-canary.3+b235819","@types/bytes":"^3.1.1","@types/koa":"^2.13.4","@types/koa__cors":"^3.0.3","@types/qs":"^6.9.7","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b2358195477eaafb0654e92c8605bc1ecf7a8350","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.6-canary.3","_nodeVersion":"12.22.6","_npmVersion":"lerna/4.0.0/node@v12.22.6+x64 (linux)","dist":{"integrity":"sha512-UYDW+9PxwLELQSTyr5LUZHa30Ps591XFhrQr+5HamnrrrCQ3EIGiIaSZUd7nOrQZJVyDT9iXKrMTwvT6JMdiwg==","shasum":"8484af0a0fdc96881bc48612d12c3b5419be35e4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.6-canary.3.tgz","fileCount":14,"unpackedSize":29689,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMW6wCRA9TVsSAnZWagAAvpQP/i8O9PdElyIpdNRKgsYj\nwAvDSrVeE3PfDEniEZtwVGz+SyRnCcCAHPrFVIzZSVhvxtoExXeo3LxmeRpe\ni8s3XiR+nUGof/mCvFn+wd0qbMyylZPwKpNrg6rPjMKloRro9pyjbm80j1pf\nvFDSqRuuPayz3jW6BaE+x6wRZnrDhZDyrA2JhIJn3NUuWsXPuiprgb6W61eo\nPK2gxZj5M8hI8NH6N0e5xClnIKNDCG1Px8+eeZEVxlSpIdieFuA3Wrb3gzy8\noT/QR1tRtsc9wX1AfGR2tfizZMgh+LBVddgcKpBvPjgK8T6MHKz5Pf17gAph\ncaXuhJ4mpUsaLDNXdZVUqxd7s5Wp5jKFC0/YsZkHkDNNAnPEw3jn9Ds1G6GA\nOlRFYTLYmo1MdPtvzZATYtsqWvEWDM8S4GfRWIlha61NUD+WBwYePxf3TYhG\n4lCm9ju5MKJzFePG3E0dF9/yff81RiW1jvy9+6OQxAhPmsN8WxjW6zpkTgFY\nvv1QHHHzFdNuchNRYO+xqEunx0ahqfNOVQVWnx78Xz9QBbfyO2VzkirWVpAz\nLuOGoQWu+Gik4mLQ5LM0wWcFFOJP9xInsrL3td0E/2lw1glLo0c+rNWhRkxI\nyDM8oxW5XsUGZRpikCSXwdNQMs4oCjUZC1Cz6ihFJzPA9pMYiEoQHrPkIgh+\npYmp\r\n=X+k7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHSstCcHSn6uV7U+d0LXbqbsJjhFHEr02clq5AMzJziLAiAIs2yN098hEL3u7t1KL6PRPLEYeGzg9yKjLh412b4Plg=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.6-canary.3_1630629552650_0.44595167108891065"},"_hasShrinkwrap":false},"1.0.6-canary.5":{"name":"plumier","version":"1.0.6-canary.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.6-canary.5+dacabee","@plumier/generic-controller":"1.0.6-canary.5+dacabee","@types/bytes":"^3.1.1","@types/koa":"^2.13.4","@types/koa__cors":"^3.0.3","@types/qs":"^6.9.7","bytes":"^3.1.0","koa":"^2.13.1","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"dacabeed10c65329eb768dfbabfff4bbbd36e915","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.6-canary.5","_nodeVersion":"12.22.6","_npmVersion":"lerna/4.0.0/node@v12.22.6+x64 (linux)","dist":{"integrity":"sha512-+0sZnna9aOrYQxWRebu75BW8O0uDoJ/tQHGWPZMmBUoB+rNvTK8SUGI/bFK+vcUZGXT3gK8B6rWr8c4AC776kA==","shasum":"da316c3e106283b89b84c725a227a7cd8380fdb4","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.6-canary.5.tgz","fileCount":14,"unpackedSize":29689,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhOqqUCRA9TVsSAnZWagAAIwEP/0yz+TgHuSMdlYyo1ixu\nYLkYNhkW/P/yDdPRZ2X6agCXhDqBf/bGdqZdfLtS94pogeDClD3gVwqUQmGP\nJ4lawBPbBSsOw4/0jHyDum0x4+JIk2l7zcv9fp7p1M4oQ4o9TOuRXXF3jUoE\nYFwoY65YRVb0s2eCCFwT/W3P9Sy7jksv6/nT1Wx1sPsk6hjf07EuxIbzXQn8\nV029xO4YtwRLsx7ZJL0+/k3Yz8/IBHs9yN4LNRXBeM4nz8rd1pXk0ghCv8qy\nM8NOpA/tiX/V4IjG0VqpCW/FdYaajgfEwE7Oj3SxdJyQ70iAK+OYvrvRD/PV\nsV66+ShR81P/XxUy+bG+77W7LcGe7ZI36dlz3gHMUYvK3TUj3j7rnNM/ET1J\ngI1a0QmcOLiOsz6Gc/8serJQjBLcZ1I/KCOcp82qCUCHKFT4lAU0SSVjfW8c\ni2vAxMWvrEINVR96YtfKn1NdnbTbVmAxM00hKgUje4/gO0AZ+k7vumm+eBmK\ncMZOceM2G/boHA+WOUPjV4bN8+risFpRUgwsYlCSrXQVOXw9/GOFhF1rOpBw\n88xO0JsDCQHbrWkS1i1cLWw8tfIMjTZP74JYRmlG+aB2d0hY9JXG0DyAFcSh\ndm0KAsxuzT3maIyQCnivAx9llEJH9iiXgANWsnhm3ooePzIA+/6xmeH1xGOW\n+Rgv\r\n=hA00\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDq2bi1u8NCN0xw5GUvJtmhj87PC8O7x3BKFKZd/N48mAiANsHDZSRfjIXsrc0tAsG+it/TpZyOGN4v15BiLVQIcaQ=="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.6-canary.5_1631234707967_0.1443555073600522"},"_hasShrinkwrap":false},"1.0.6":{"name":"plumier","version":"1.0.6","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"^1.0.6","@plumier/generic-controller":"^1.0.6","@types/bytes":"^3.1.1","@types/koa":"^2.13.4","@types/koa__cors":"^3.0.3","@types/qs":"^6.9.7","bytes":"^3.1.0","koa":"^2.13.3","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"03a338cbd3949d87760411059caf56ccbdf9d6b9","_id":"plumier@1.0.6","_nodeVersion":"12.22.6","_npmVersion":"lerna/4.0.0/node@v12.22.6+x64 (linux)","dist":{"integrity":"sha512-lRM6GRn6g/2dove3O1TMpDsWLww3OEKTvWThKqVhKcHdks2M7F5z018shRzYAEizyIHVPry8yiAoA4B0ij1Sxw==","shasum":"6ab9bb2b91732dc01c85305acccc0b595d1d150e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.6.tgz","fileCount":14,"unpackedSize":29778,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh220cCRA9TVsSAnZWagAAmhYQAIEIOa9bOiu4IdlKm1uD\nfekEYlwVIJxYe75Zl9kUSaPNRuaNcPkTMGB1NfsJk1SyyUeJo8jB2+ohtukP\nn24Xu6VIeIXOWmK15w2KDRHXLagVjxClCgWYTEJlZCrsU6WihubyhYXQIeJd\nhWoUIBrBdfKSUnO0d/veNy6SYlqQRhPYcAQJWbJy/m4ItcKdcgvpIGWNjB5v\n6hkiU3HkVnyPq3EzcO0ymmh9J2uJLzFixKXYKp+yxeOIHiKh/I49AnNn34k+\nPMs+66tkjoB5WN7nPUYqGaJDnkFkVo6istzH8kNT5MNk0hbXI8KGQi8p4WiE\nsffom6TZOrSrcKfhRtVhbiswIKsb/MCxlSifLqcPObIOZ6yw3nMsXpu6H1N2\nW/OFffcFitZMWIEvvj5buh6beVvOfsjPIyH+L/GNcPRuIVlmaXkr9yR8oWT0\nF6SczlgMP2tD4pWZ3UNVidnjOu5SOcjopDDYdPBxPA77fBeDxir9ubunp/oY\nrKIFxA4/EmtL8eKIVoLz1GuIFQezTI2TRoLmKjYt4TGzhvGSZGdXSRLqBM26\ncGzQNgq6thYHIRlzO2Aj0tyijnzioJ8dt/8TJxKMO2sNdG/Eioer+A/zX+vd\n6pkv66iZxaCQxOmQgfOpV/np8dnUvcujAeAA+ViLSTU/FsX+x/S3i1AkLQJE\nLAc5\r\n=VQU4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBeVjocKuOk5xS0sp8JLRO6shW3AU/cZ3Eqv2SfrYjM7AiEA7ExfiLLcowb8AbS4arCeTs8Gut1iA0A4xEUC4fUmIKI="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.6_1633323382576_0.6780462125165276"},"_hasShrinkwrap":false},"1.0.7-canary.0":{"name":"plumier","version":"1.0.7-canary.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.1.0","@plumier/core":"1.0.7-canary.0+03a338c","@plumier/generic-controller":"1.0.7-canary.0+03a338c","@types/bytes":"^3.1.1","@types/koa":"^2.13.4","@types/koa__cors":"^3.0.3","@types/qs":"^6.9.7","bytes":"^3.1.0","koa":"^2.13.3","koa-body":"^4.2.0","qs":"^6.10.1"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"03a338cbd3949d87760411059caf56ccbdf9d6b9","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.7-canary.0","_nodeVersion":"12.22.6","_npmVersion":"lerna/4.0.0/node@v12.22.6+x64 (linux)","dist":{"integrity":"sha512-KRqifX3J1LDqrRx2CV2eOoOWrz96qpPWUVkfw+IVwpIlBrvFdjWsPoewQ/uTG1twjmh0MoKiXdPc+cCSE3nW6g==","shasum":"4ed449f0027b56d94053f3be10fe9f3723406262","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.7-canary.0.tgz","fileCount":14,"unpackedSize":29827,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCMYIxXCyHQDlc2qMZGZtQiEOFag+ifXazDNSg7uogJKgIgdg7yqF6WcIslftX5gUjevYcNehYp89SvqxpbKxYaSlw="}]},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.7-canary.0_1633653922168_0.8918021954297737"},"_hasShrinkwrap":false},"1.0.7-canary.4":{"name":"plumier","version":"1.0.7-canary.4","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.0.7-canary.4+f65ec5b","@plumier/generic-controller":"1.0.7-canary.4+f65ec5b","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"f65ec5baa14af9b34793a2ab74f440233f84c6d7","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.7-canary.4","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-dPWg+UdPXHwjiwXvAe7wu+Hr/5i93AVztaJRBVlb+lt10c0+9kdBA2tHfiKlF3i6cyCDPcGFgATk1m++mpBdSA==","shasum":"4da14fbc74de4b6cb49a6e48925a26946f6fbe08","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.7-canary.4.tgz","fileCount":14,"unpackedSize":29869,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBCUUyEzX3aEEpsYG412GXb/79i/+2Vk0iUhke6kVt0QAiBPnPmrUXi6L0TZltUH5T5Q+uokKKA0pVxWwrPsLddm6A=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjkAM8ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq9uxAAmObSR/tRZJIXFcU3/YJ5MJEZnpq/6EGMACVzUHIYQcaBTjVN\r\nXCQU+OeLC+XaVIzkI4WW2sQeXdqKhCAtmGO+lFbe4ssMr9RaR6czuwrfBS/n\r\n/AKx8BHu1HAGbjv+r99IMwlWaKKpSO7QePxhmiIb7zojv5RFq+PGtVOCMlT4\r\nKK6hwjcCPLdvzn9P6eJAcEppvgGXZUf2eAdPoEheKSOccoRI4LHs4EHecbLb\r\nQ6T67QSxdvjVCpjXjDn3LZ6c2CbZNnr40/sSS84BAK0bi/kTfbeeyRypJsbi\r\n8GASEo7DNYQRV6YFKkL2SxsM7QBQuqrRT+58ypAtxauTyeXeebRWBTLhnwEu\r\nWFj5UArkkjQglbAraCFkOB9C9CEAFNa9gyjTMUBu3J7naf++LMG4Vp3KE3cq\r\nCd6uRnnPT0CmHLo3mlEJDDoTWwcncvwxLEqS4lB/IHuC57yv7Z8BHLBnf0O0\r\nbLDzC8fk/Sc78+Cnwer+aQhr0eyjQ7ue8zrM2EfMwlq34xgglMmKdrOG6foC\r\ngx9lLU1AML0yGoRVkXRf2WuZnX9od8ddRScpd5Vhi1kizXbn+hA7CVQmSGx3\r\nMwMO1RVxilOnUyttXCIJO79GpnqE039m3jqQiSpyHfHeevlUw6YcgPguBOIp\r\n0fX67K18WB4Hf9fUkU42+F+BH6DEKVPXM1U=\r\n=GfBX\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.7-canary.4_1670382396549_0.2757863984247235"},"_hasShrinkwrap":false},"1.0.7-canary.5":{"name":"plumier","version":"1.0.7-canary.5","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.0.7-canary.5+2cc9e82","@plumier/generic-controller":"1.0.7-canary.5+2cc9e82","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2cc9e82601b70703218da8e09bd38543fb09332e","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.0.7-canary.5","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-W5iVFFKDE1/HtQ1xpr/Tqlq2MHO0GxO05IgRPpl5rkfmyn1MH1qBtiHtMx/h4xtx1/oAuE7W3D+aK0el7vErhA==","shasum":"3a47138384b5cd58aee4af38cb94f0c4e49e298d","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.0.7-canary.5.tgz","fileCount":14,"unpackedSize":29869,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDS1GLbDlRev3Hoy+MUasBlTl6vQYxF+hFDBT2UWxUTUgIgVVqLMN5YDPdipAz596dnAHYbq7lEeY+bzhnKVBHE7Uo="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjkGDoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqQEg/9GLTQzttspyiFClDRBKVt2kEKu49dF0ff1lcrjzxNOtH885L8\r\nyx+o15n+vPb60QQoGaainTJewz2ExWcyzCuHk0STWfQq863lAUeI5ZOuOU4B\r\nTYGPvZf18loPLMr+c+OzfDtLM0Bxi6ULoRoHkf8RKwnRRd2S65ZP73SNk94C\r\n4mhXSW1jvUrK2GPDak2Be37bg5oV/AnOgYVk7F5ocWDSYJx6zm/7C7t9shKr\r\nKEtztt8PYrHJRYEPWiMI822eqvH9Vjaea2Yn9bzhTujdiakpdUDdvh9B+TXp\r\nXdWFmpQmgK16CI3xcQfvtktSi3dKQ2/iV6jGn0f4hqP2qfUqqJ3nzHfdVaxt\r\n9O+a5kVPO9FjITq7TnX/L/jf5ToZ4GedOS/9BLwD/o55VwmjhQ9gnxGzDSLW\r\nvx0B+Q9eiWygom08WcMnRmXzgxLKiTHWXy/BRP808u27FPqQJxFwSCiE8yfV\r\nlW1XgPU9E5N7OfDxUo3JqGwJAYHXAp5teD2+wgUS8TVOZeAwkhyhdxcBXje0\r\nM5JPpwB7mFYFfYFrVUr9rFJTNFUwomwRpX+2oeOmX641paxoarXG74K4216g\r\nU4uFSD8caHiTd1ksecPFrQOhR66Enrgx7IF+Lc8utVul7QMQqujrr4D+jV3O\r\nnfU1GyWcb5zI6wXhuBIdzsV2lrUgxZnlXbA=\r\n=1C+4\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.0.7-canary.5_1670406375854_0.5713196358304327"},"_hasShrinkwrap":false},"1.1.1-canary.0":{"name":"plumier","version":"1.1.1-canary.0","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.1.1-canary.0+719d23a","@plumier/generic-controller":"1.1.1-canary.0+719d23a","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"719d23ab41785c02f0e851a2074c86ee0afe6dd1","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.1.1-canary.0","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-He3Lrio421C1Oyjm2IFkrFVxajbdiUJnmGBlnTDTV8+v1QvO4LUdHW3oFS6TRZywiChQ/G103IfUbLAScWINrQ==","shasum":"b17ea739e33dd2175e9d7d1bbef623ef9d980ef8","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.1-canary.0.tgz","fileCount":14,"unpackedSize":30006,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC+w1XiMbmAozAhMCSZbaHo2wyh+rKrO0xyLjEBbG6BlAIgKj2ZG/JavwvtLuG3ZliMTuxufV10qDKatszWzUxziWU="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjq4qdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqNPg//RzuUl3q0cdwH60gmfigOLn3JsJPTfyjJiyLz6FFas9bty5Qd\r\n29BT7etIjG+WL2NOIMC8N1yaHoPFk4isobc1KW8TUjm2tcjEoO3HOWBp3xn1\r\nd3knOLo6gqOm585PAK6Tox0uL7hm2orZDx9n/eQpYXSfO524yd5n/lNS4I+L\r\n48lJAwhNVoINLbSDjJbaB3mgH/CuUnsK/ji8Xxew+4ErW6VjZWb17QwJT9zw\r\naW/5R/cBr3FpBGv931wrBGUtZk4sQzzPHEMdS/RqsWYxDAOE2EYqjFpmVhjZ\r\nMNV50ZGMc6XXmNrBnlRPnZcD1Rp0CpsyHeAUnP1sqP+7Z8j5PRK3uM4Y6f1I\r\nkW1ozbAkXxU+OWLvDFsZbciB31dEjs3skSbDrEiyupWDEzjaUBzaK1v/jNUB\r\nSoQExpYoHk3VI+x+n8iH5D+Lcu7Ajx9rIFPdMfGP9vF0WDQwQZgL75OUycS7\r\nhMhkW5tmjcgsUkAUOeCp0zSKjhIxuZjfovBPwVoNI+d06u1VlfWtvGyU7+Xo\r\n0tsXXWtXBKjbxED7IG8fqLt7bOrmXHT5FBj3ZxGEXQvMGlyJ9u7wr3syzrSa\r\nlxAEZr0W/yIdi9FgcgTmhC5msLCnVvidk0cV2Yok2V2kEml7QaAlgdRlK8Uy\r\na0O6EG9cxmlP9WXBbBiJ+16FxpKgVT69z5c=\r\n=r4pm\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.1-canary.0_1672186525294_0.27178710223989544"},"_hasShrinkwrap":false},"1.1.1-canary.1":{"name":"plumier","version":"1.1.1-canary.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.1.1-canary.1+e2be8ca","@plumier/generic-controller":"1.1.1-canary.1+e2be8ca","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"e2be8ca167e9fc3f959d5f6b26d0aa6d8cec49fb","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.1.1-canary.1","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-9z0Zh3qfGuzoNkAl4/mWgAYN87eEq4EV//FpAbI/bi25j6Ng3nrNKDyANN9IoBSkOi2eqibRQBJIQu8Nxfofqg==","shasum":"6f5268e93bfd14dbb00d49a1f1ef3e53346b4f4e","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.1-canary.1.tgz","fileCount":14,"unpackedSize":30006,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDeRnKFMfHXZXNjNBj6GtZZ25Op48BJRMjO9QwjvwO/OgIgTQXIqq53zGYQHjJIyU526T9oT93KN4mWHophbqQ3MMk="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrCJRACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpNmQ/7BFUzMvNwPxv1+PhJON0aj4gdWp1k31DMpKCYqOwUS7S3v4mD\r\ntzSGEOTuTniHq+jg/su/SXZpg2En/wjDHBzfH+fpe31ZDY9F3rQmUG/KgTHq\r\nXjL5Mn6Xa8QV7DMLc8zF4N5ciDWNbcror4WPO9j25eu3RX1ovE2QiI9psFIw\r\nAzrKb8qPaJhU8fNsl2txOpSN7JKhR27Wju8SwVxGyz0rTlPyA2snxY8ZVyWj\r\neeN7XDa6AbsATr6/ziURfPfxKqrha7cTkS3pA9GMmAaGIMHmyNlsR6sLoPz7\r\npY1yoIZV6WaMQcYhJvN1Szd2pxFZ/8UCfYEE/RuO7wW6hDis3CC/n0RAWJIG\r\naIuivAQ8lJ9v+9twLWD3gAz/UHl9VdV8h4/xi/HiJBP1nz2JL/FxdQ8LzstR\r\n0MnMzT3Mvl8IxvJXTKYOKqxwpRbAsl559JOGhZqgUdP+yF8StuLVjCYPobHL\r\n8mXArEocsrM+lXfDbyKHYnipNrekmlcNnGEBWbY2+x3faPqkNL4YMuGWK8rd\r\nQeEZT5sag74JN/oHJFYAgMC1kRS/PbXIqwExqCumYgqWK0CTtXznvrciUdqn\r\nSTnlTk1SsXJtWMUxRScJ1PFHyVOkBb1GgjG7ki1B2xpfU31VfkOWgYZCHnvC\r\nMSp761W4ZEWt4mvGLrPuM73xZPyqU3rAp/g=\r\n=3Gry\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.1-canary.1_1672225360913_0.9802949228784961"},"_hasShrinkwrap":false},"1.1.1":{"name":"plumier","version":"1.1.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"^1.1.1","@plumier/generic-controller":"^1.1.1","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"453288cba05d98a4d41b1a461eb88dbd0dfeac96","_id":"plumier@1.1.1","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-W1frzhXuVh4bIxsbXz/7JoZiIRStRYrLpk999aCrcl7HQFnC1H1kwWEKN8jwC/qWaaR6+roWGahrOVZ+JRNWDg==","shasum":"d68796a3803e02aeb1a346fcd3af8c71e5ae1006","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.1.tgz","fileCount":14,"unpackedSize":30095,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDzZFW8Rw5G33/PexnfomvPO7UZZVp8Vj8lB8QoKgdbGAIhAKRRjTHIg6sDfh0MGoFIfHWDt9f4MwSD93ZZjDEKzhzP"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjs6yZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmruig//egc2HbxrrphQqQVGnJmanKNftjeYhHBVJX3Vu8qNOdMvDd8K\r\nxz/O9GA3mppY08lq4IvuB8FyFyBLihnL7rTutEEUqpsrstLmrLINxwskimC/\r\n2yvVUnSmfzq/NFf6z6d3WoW9D/1BEygQndCbZlX/Yl+EVwpiqbckomCafce0\r\nxQIpmicGczPpBxayrXilwahAl5zSWLZddxKflQ2s5i/Ci8Akj6k1IwN1N5EV\r\ndIhbVyuiJt33MCclY6jHLgxlTrNiCsoKhT+ZChNEwM/7qSotIufwKValCq7m\r\nxi3EczcSZC6OJAjzwo6nYObYVio/KLBU2TXsK903k3Nptrd1x25Nz1+79p40\r\nfXsjdXDYKjbYWw+KCJuQP3u0VegZCarhg+qdVXE26eIkh73pvzJZoV1rSs4G\r\nO2oAJBh8KkY7JYZpQnohA3GcxRDz5mDmPO/JpqptwdkOrKKbviU51tuQAH1D\r\n37V0hTLlfEHdGOwu8RUPOef2i3g86iE5CcdIy9xdb2MEigTWMlHWy+XjNPDL\r\nBo5dyEMj7X808J2nkZYqjDf6migpoRuGsp7g/d/PCI9I4JyzPsbAwG2+NrPy\r\nI+eteWcnUYS/9mV/6urd4wU+8wBvAUB+MBi5C2ba+MvkhnZB/u3QanpP1cji\r\nK/4ORd/w/mLz6yH/fUrFgCY8RigpZkyUBg8=\r\n=kJwl\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.1_1672719513187_0.4918944099166578"},"_hasShrinkwrap":false},"1.1.2-canary.1":{"name":"plumier","version":"1.1.2-canary.1","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.1.2-canary.1+c9bc774","@plumier/generic-controller":"1.1.2-canary.1+c9bc774","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"c9bc7742f7f3f4e910fb3f097a1f5d5780ae2442","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.1.2-canary.1","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-MEmzpjvckzyjsNgOUcbOgNr6yJ+WKMadNEDfqwC+9hiPWK7xGboyv/BngMu0ZWsjaq7AxzuVkqOZJFdkYTj3iA==","shasum":"06a5702b845f53653532e2fde32467c1aff52fdd","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.2-canary.1.tgz","fileCount":14,"unpackedSize":30144,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDnNXSYNYtRgzYX3lyLIpFj/lYE1UNbjLvzY+ks7CmUKQIhAKyWHrGkwf9xlZ8KVO05lphsBZdQ1azbWOBBTesSgRvn"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjuPYWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrqLxAAlXHL8Q6iOZVJLsHfBm7xABFJD9tJkoBVXrXQ2tOJsUjjIAik\r\nnX44w35oyjv+dlMuq1Bz9exc+8ApgJs5sMm8jbjFgZYnNPoekrogUxt2c6si\r\ndUwZPIpDgCCUHPwRSVHa0lXz8z8tOhvWAh791CH2J3ROB/AYuYqyX/KpcynW\r\nfxKrB6RSDGAE8SvSyigbsT1ZXekw/1Q8FAighRdH+lP//PoYWpJCfWw9WygT\r\nQolPnlA9wdf3aooNz0rHJAUgOxy3EeKPSDyjFQGylkqYkc9aC/9kfkmrD+pI\r\nJZgxqgaAMphL8rRaAPO1R1+GqzeFZiapIi3g4lXr0H2By3pBejoyZA+0Dhlt\r\n0YxQSN5wl/3CBwsUpJcjzgGIADHds8xXMeLPf9HefY0feWPX4uwAQMYHlgp7\r\n6+qtrfBNqbwnZNE/uZdun8fL143LUKlozWPX2ZRK+p+JXKKPBwIY8I5z9FE3\r\ncTMLsob0Su9ukwXC4dg+pBnnDt2vGGniWETdMylOtooi5uoNB40SurWbhV3n\r\nXlBdQ3mJzBMg4SjxsP7vr2gkTt7kU/X2CECdIeKbU8yAyRhB/4hndMtUQfyz\r\nPOFEcLPa2ImipTeKXGD0CskRtrQpJefTn+8A8SN3QtcHIO+zvzm95iSsn8yB\r\nUjDGK66XH8BhafQsYoUwfJ7BZrRK+u5UFT0=\r\n=ZlHd\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.2-canary.1_1673066006675_0.4598481102671472"},"_hasShrinkwrap":false},"1.1.2-canary.2":{"name":"plumier","version":"1.1.2-canary.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.1.2-canary.2+1e6f54e","@plumier/generic-controller":"1.1.2-canary.2+1e6f54e","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"1e6f54e893c16778115750d2f3683464958b7977","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.1.2-canary.2","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-RVR9BvAqoWkrBXxDMizHZnelQeWnPxvmJHrQSR2Cczt6I9+GcvYTw0+DwAiVjZY3cclp4zfTNnXgvSs1bEPCMA==","shasum":"1c9f6c290672c427dc3f4d07199baefca22d2366","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.2-canary.2.tgz","fileCount":14,"unpackedSize":30144,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHypGUKHm4GRJXD5uHbO4N39ooD3G+5NWKgRHzPcmYxeAiAiAbgRmpuvbEypdTg3FDM/Dpo8i01yjdQJ3zrIg7HOxQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjv6eVACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoZBQ//QF0kFB78CFvJhd4OmzJQsHsoWJz8nwWrKcCmj8ocmKtMISnW\r\nzSZrOEfra5t9rE5NlKdzr70YSJacNFwjAWHUxIBtmpPXC7Mse5VIbrj4nTDg\r\nw8sTYo3nkZPowrh/XQzECxviuXx9sEPFO9I7Q+/Tb4pRUCtupMCwYw8jx7Ou\r\n1zNAMIJvgNaER0gFpgLJ0b0EYev33rYOKRLJC4uDC0bGIwLmLfzk6VNjG6dt\r\nUzry3a+fPmEKFnlfMEOlWDkJyrh0/VT2EU/EMkplac/GpAqrKFH1s7hNwt/R\r\nxg7To8ARU/pQ/9vaHKfF2fBPlDRmVdyXyHHrERuG6qqtiUmoE7SUca9YIH/0\r\neQsYgMZfQrBDzr3jbT8u77lkHmgCdsmSKLm9HTXMEn1AKdgkc4yUbmSlVpN/\r\n3PS86Sr23d1rQY9W/6ZuMUJ+cXwJpyRlJ/Mgsp2qPXKBbZ0wKzY0TbQP+3X6\r\n+VTsgt7byA389a7QTjhXPr9dYiS7JBdRrqsdi97P5hIsYVO2gQHZtnUtQX+9\r\nvlPYJVZPG9+S9j04321HM3b/4n7ZZclEBZ1xP3CvEsjvG/22lesHiDl2mgc1\r\n0hxoaUwvdtXsiAfcBwxFPMJB6vNSL2MkwdOwl2Mc3SwzpKvCOVPDVwx83iaN\r\nPT0kr9Ym/mN3JJTjCuz6sc8e7OG+GQ27zA0=\r\n=npW3\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.2-canary.2_1673504661701_0.6452584802254866"},"_hasShrinkwrap":false},"1.1.2-canary.4":{"name":"plumier","version":"1.1.2-canary.4","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.1.2-canary.4+b569e4e","@plumier/generic-controller":"1.1.2-canary.4+b569e4e","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"b569e4e6adfd0d70121164156ec60b78051d3b08","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.1.2-canary.4","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-vscofQjcC1/vDy3G+7rgK0Pn5GE430fQH2+aPMELViuWkRDXejX8Vpx/jSZliaL0gKILFWJfNpj7sLkamY3Npw==","shasum":"0d7689ca2e810c3e7be90ff2e40cb06fe9a16d97","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.2-canary.4.tgz","fileCount":14,"unpackedSize":30144,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBEaxvRboRGZF582N+rUhymhE+/vflJEz/3KiEX9vdtRAiBMapGWj4qfLl0q791YPWOL4F+RPAylUXUmOIU71X7czg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjxHn9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrSqg//XBtGBYxVh7ePIpOV8XhsNPcqhajSQ/tZ7VJXXY3jbxY1gyQA\r\n9pLrnG0gN1JPyat1GLLSeKz12nucePIaw6pyIgg4ZdfvSk7V4Zud+i9GPspG\r\n6O3Hn1+3EpOkewcv+kI7+LtZptuhY2jkavzNLZXE0U1IPFRHeowuTCpVPewm\r\n56FSpm+MPlzMdClSrTRErqrobmdR/cMAEZx6pvnRDJTgggLMK/2oQRpI1Z/P\r\nTn1+JCt3CITdVORH970K9pVAKTFXI4tg4C7HrndTZecaKSCu2JJHfIyZVdSa\r\ngT1moyJJq0HbHOoFezZ19r5zqDf3FXgMFG6q2XAr784Mlql8uAtX4BhyjtPg\r\nJ+Gt5CnhwJ+p/pjYcR9kKSdzv/t8qQ53u4PLao1v4DBWe39OE4zHjA6nhE7W\r\n3B0MM5Z3YV36INKrJtoKrEiygC4sIKSRnZYnXBAEcvAioXb4Vx+4/P1lvzeo\r\nmtkFm3pqp69Ns62D15dv8nNMxV9WoqtmJVAIG7brpOXhiN+c6cGItuP2GEY4\r\n2a7XlEO5yXQp2RirCSPpUZPmz5c70ho1CubLNCuewe17mBwSQKTDh9wVKlxv\r\n/sLDyb6VagWvOu6A2nLBr5/jBgF3QouCYuVguq3BNyC03dhZt30zmg1TbcUx\r\nWvwYVOV3rvL3XrlQIY9ugHXNE5h2nHDGO30=\r\n=IMsE\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.2-canary.4_1673820669183_0.8995712948699162"},"_hasShrinkwrap":false},"1.1.2":{"name":"plumier","version":"1.1.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"^1.1.2","@plumier/generic-controller":"^1.1.2","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"cfda714b6f9048e66d3d7a5a1f0a51310bfc9902","_id":"plumier@1.1.2","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-H0j7zh5RQn5dl2ZIBvmziNVymX+5LldGlQ0Sl87xXQPGAi4gwzAn7D3r3N7Rwt0gjYGPwxn0G/fkC0AGPfw3+Q==","shasum":"8c5e52d1247f436cf5b6229364a25cec49552647","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.2.tgz","fileCount":14,"unpackedSize":30233,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC8pVIHLPy4gP6BenvymeOUd12lAhtSbhT0X8/QgtWcwwIhAJG06j1JsgT+vhgLUgUFL3FJbsTa03mbGRQfS5Q1rI4t"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjyd2GACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr+8w/+Lml+5V6ZZvaUTNhbGcRmLCNsrub8PaYa/iCnBGOujKsPda5g\r\ne9xV9OQb9Zu+qm61qnNfE1BH4tcUCzg9iO0/jUADjbkDg/w1sYv0WT8jl6bm\r\nt/DBSzV13oy6+sEX1kO0nAzWGe2lUSni7kvJL3DVolWGJBS3MAbNfgnNp4I6\r\nhdXMDYru39bzzuSJ0VqBTOWhclVRE9VOvSE3JqUIkOQFHPtel9HjKHibcK+n\r\nYlhA1jHJi1B/psNK18b7MfZGM6VnA5+zhwmfIojWYEufdMUjvBQNc578pygQ\r\n4+UKMZ4boqsFRxNO3gjsTNiTAqQmTe5AleYeximSwMJVDPrOkr6ogRPtH8PN\r\n5eEO0nZkyPiX8qf9SIQbYY85HiJIrZqi6UVAFaw+b4tCpReYs4XpZIhvFngB\r\n8xGmxR0pwE0VIBAo1f9SCht09G2BgMf3c3zWRGl1vOsXrt970cDfW1R8VzhQ\r\nGU6Kog1BRvjCdQXbsjBRdJU+miUeEDZAsLsJbs8uTsj/VmTHuNA7KD1CpT8r\r\nW/k2TnJPhgcQizf4FdtUJw0WU5UMCNJRTJ2GXEWO7ndtMM0iDfdvCWUreUuX\r\n78SdNrlMEQXUIAQN+2/VTaPw3l1bIkLPEz0bpX4wCR5n+1GjWQDlz13u7MRY\r\new+OQV2mZnHr+isNwiYHlnxy56SI9GDmoPY=\r\n=9bU9\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.2_1674173830141_0.16228090928438244"},"_hasShrinkwrap":false},"1.1.3-canary.2":{"name":"plumier","version":"1.1.3-canary.2","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.1.3-canary.2+2f07d85","@plumier/generic-controller":"1.1.3-canary.2+2f07d85","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"2f07d859d310ae36c017e8719555f56a28035c63","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.1.3-canary.2","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-TQQPxMMBkEdjpmV1T6wHJ94vn9Bzt+kLwtwJU+aOmwn8OigXI22siYa/ymur+aGr+kj6ALneBTY8FnRdxlFI6g==","shasum":"12dd8bfdfc1bbce1d7b3e781f383b0120a64c609","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.3-canary.2.tgz","fileCount":14,"unpackedSize":30282,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYuuj1n8TNghqlw/L/YEoaFW98P6ZzdUaK8gzLw72cBwIgUfvobnvlny6o26zauerh32NGvNk/xsXiBjvRDbeaEHQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj0wu5ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmquBg//RtoSRsyL+dXlYYtC7eWHTafV18uP/s8X63/l71QUmElbogqE\r\n734mm/b+0tdf7/T038UcSVt5Yk91YImAIS074ydJkR9QpCTUDIlGHqQoN24B\r\nSQuApqWdNvXSJczzltmCKSHrA5qvy0eL1ckROn2IXFk27Pu9zrupDlzsZI8M\r\nvyZp9fUiPkgIE0vO69HYyGdneUvMN/MCTezY4OKadg/yr/CZGRhkTx92xfCL\r\nPQVOTNiLXAoGoOxaDH7tVhfh1OclT+key8zgeMWr9qkndL6btsh1Vmv+HJc3\r\nxQAe0WEeUnaRBkFDC5Bs4V894pzQYDUCSEDmh+XmZo7KGd2pDBPFpfOViDkU\r\nhIpSqGSQvcVv4yx4OHfF8wYiXqyZ4JitrNax1RXH8mVZgsmoCWKjcgFl7fik\r\n9Elace9DnaUTTqWN7lsETTyaS8IdCSeglG8LdCzYn7o4ejjRSQHvfyH8gxne\r\n9J0pM1csXxfFPGX1gcF9L7rXjpgoQrgegBqlmh5UDzzFTXzK0pcgp2CVgA/k\r\nrcg79VuPur+AV4jygfC8BjeNuWeOS68UkkQ3ysdvOqOr1RUby+1dgbTOCEEI\r\nqCFm0UBK3zvHPNRyATVIDUycaAhk9uzqdK20+rkhpqQ4NrGb6ll/R0aA0wSL\r\nCN6Dbm6g2/9Yn2QZgGpRawtwLeNoqG54ODU=\r\n=uma9\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.3-canary.2_1674775481488_0.7094220121810653"},"_hasShrinkwrap":false},"1.1.3-canary.3":{"name":"plumier","version":"1.1.3-canary.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"1.1.3-canary.3+0fac589","@plumier/generic-controller":"1.1.3-canary.3+0fac589","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"0fac58948eae4cd5c03138b1d2ca414deade229b","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","_id":"plumier@1.1.3-canary.3","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-NjGCloSIhUI558mq6kDM1k7lPZGTt+2DzceCbsE4i1MOixrKZBKRMB2zlwqvYnZe6mOqug5QeNJ0eoPPwWAMrg==","shasum":"75951c65a7e2c0cfd8f8981319fb6a5de366a34b","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.3-canary.3.tgz","fileCount":14,"unpackedSize":30282,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG1UkMJubOSz/tvOvcYXGDcuHCbQ5kaKagQfXIiT+RVGAiBInJd5t/LXOC4MQFhJJU4HCItk8n6TLQujeve6FPxsmg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj1EJ2ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqogg/9GADQ7ABiSx8jxbkEdP2hrVmQOA6eYPQjyw+nKq4yJ9//xBVD\r\nkkudZrI6zuhMCVAL/9X5Ma4Z9nls/0sO6cLQ4zmovEuz09PZirPUIBLE2rET\r\nwgCaWVaNSccW5FpqO+hlvtT7mqNG1EgLWTsgWeqk18QCDFqwSIbLGyhlcqEB\r\n7/YZU0kA2rm0hoaJNr4EF4844n2o7Zy5xBRLhXx07qePQJxQ4pm1e2Rz4ycY\r\nMJdOoS7oRpKc3hiDAosC02VmTobkh9Xi57FFHiw+9h2T0oog+0HVk4fvn0uO\r\nBlkMEQ/+E65X9X+sNB5ti54aqWLPWd08lFOrzLd9mAd0F68ScN2hbN1t884I\r\nStkK4D0cVYSUi8vj2eceZjnlCkxb/PWC2Sy1iAAlgDFMjT7AYsxHqPQLleNs\r\n1gCHZk+BXuI06PwkcJRlZBe79lmR6VY2cvaIF+et81vfBeeQw3nBytYvmtwg\r\neTTxKBJZ/pRoZE0EefODRpZsfmsKLEXDQwVZS03gdskKvPCNvW9IcPfrGXm6\r\n2ymp4mofHGxfEh95AvliDiMr/0jqM+q0VC2Y7N5TBzk6rsbf0abz0AqNvSoK\r\nIiDc+f6ymUxJc2EUvUKEaGCdxYV5Xa5Tnfz8U77OamGAXiHukwFdFpK/zfVb\r\ndT6kUsgf2oiOH/vA+Ah0d0w7gUUR4c/NPBg=\r\n=BDXS\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.3-canary.3_1674855030247_0.941437810415271"},"_hasShrinkwrap":false},"1.1.3":{"name":"plumier","version":"1.1.3","description":"Delightful Node.js Rest Framework","main":"lib/index.js","types":"lib/index.d.ts","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"scripts":{"compile":"tsc -p tsconfig.build.json","copy-readme":"cpy ../../readme.md ."},"author":{"name":"Ketut Sandiarsa"},"license":"MIT","dependencies":{"@koa/cors":"^3.4.3","@plumier/core":"^1.1.3","@plumier/generic-controller":"^1.1.3","@types/bytes":"^3.1.1","@types/koa":"^2.13.5","@types/koa__cors":"^3.3.0","@types/qs":"^6.9.7","bytes":"^3.1.2","koa":"^2.13.4","koa-body":"^4.2.0","qs":"^6.11.0"},"devDependencies":{"cpy-cli":"^3.1.1"},"bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"},"homepage":"https://plumierjs.com","publishConfig":{"access":"public"},"gitHead":"548e038b465048dd187866701ecb5fa81ccee517","_id":"plumier@1.1.3","_nodeVersion":"16.17.0","_npmVersion":"lerna/4.0.0/node@v16.17.0+arm64 (darwin)","dist":{"integrity":"sha512-FGQo6oWBToq4sU3XWS5EFMABKPmOR7iZhtZcJnQhW3Fni7iOLy66hGQu2jVwQOoPaV0fxNBGNsLoFKqeTe3W8g==","shasum":"223322b0e626b73442d760e998b3177e36ab7718","tarball":"https://registry.npmjs.org/plumier/-/plumier-1.1.3.tgz","fileCount":14,"unpackedSize":30371,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHPZtlub8Gm+mMv5Tvj+DL1ru0aycj48T3YFjdyELbnRAiEAnym+O6Fuv9JgkqJGmbUB1TQfaQpB4e1IM+zn87rU4a8="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkCpcwACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmodyxAAjcG6FLSDlCmE5hX2qWzUPG6OBrIMvNF3NcmGWUrvLN4vBght\r\nl/pd79JZDkb7lqLEqPTn5hJV+FH3eScRH2pmkl+e/5OiAHbX5NGzENJaVkVa\r\nMWHmghyDCySgxqP7ECxO5NRaC2Rp86EAfZf3CUReU7diU9cQFsNZ1Rn/R2ni\r\n7Ti0amZvK/dOWEFIUNwZESa9Sy+/VxADoPGi0sVehXMJD4u7Qc6cEAGfHHhQ\r\nwPnNocSlS5bms4RGF3TQzED3MwRpfIF7Vp7dGfEWEbsUBvwnxl4eHbP5Q2jT\r\nz1TrdH7ZdHcQxhSZoxXmpvgtRSlAtqEwaOoCpTXVQxL0/CXbqodUjqoBZcjL\r\nLWcRAW/LsufJ1IEHjGxRzsyea5WtEDssEg2M2BVjNTknWr++S+5AbgpMpOuu\r\nzKEzcepfWMrkOWh84lN/B6LptwQzVQ4rN9+IuWuJWjKFw+gPUUaH6CLiPb2d\r\nhUB8iVbbTH4Sa0dyyEoSyJ1cCYqW0ff4Uyp6l5tEHisEm7lBVtXMYZDO0BG5\r\nxZBCIIxKjk+r3pvbGWKNATBGqjlObIpmwWwwZUzdeDhrvXokRokH8+T3Gj+b\r\n6nycMAjhIIIUyT2OnohJUdC+mt46DegDnHkrOYCbY1Bro+snzBGaXwQhjNlM\r\nXiFobSjbYwdsrmT3c+9lZPsakZCjcTG7NoE=\r\n=1O8I\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ktutnik","email":"ktutnik@gmail.com"},"directories":{},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/plumier_1.1.3_1678415664043_0.9601484472385853"},"_hasShrinkwrap":false}},"time":{"created":"2018-07-04T11:37:11.501Z","0.1.0-alpha.d43b984e":"2018-07-04T11:37:11.596Z","modified":"2023-03-10T02:34:24.278Z","0.1.0-alpha.606deb1d":"2018-07-05T03:37:45.227Z","0.1.0-alpha.8f9a56cf":"2018-07-05T03:46:07.557Z","0.1.0-alpha.7335c00b":"2018-07-05T21:02:35.593Z","0.1.0-alpha.ad52811b":"2018-07-06T09:29:20.226Z","0.1.0-alpha.068e9f61":"2018-07-06T09:36:23.939Z","0.1.0-alpha.b123904d":"2018-07-08T03:08:57.091Z","0.1.0-alpha.79af24d3":"2018-07-15T22:26:34.616Z","0.1.0-alpha.caa0578b":"2018-07-15T23:22:47.656Z","1.0.0-beta.0":"2019-02-12T03:17:59.366Z","1.0.0-beta.1":"2019-02-15T01:35:22.633Z","1.0.0-beta.2":"2019-02-15T04:20:14.182Z","1.0.0-beta.3":"2019-03-14T21:13:59.160Z","1.0.0-beta.4":"2019-03-22T09:26:49.811Z","1.0.0-alpha.5":"2019-03-25T17:47:15.235Z","1.0.0-alpha.9":"2019-03-26T11:57:29.874Z","1.0.0-beta.5":"2019-03-26T22:06:46.529Z","1.0.0-alpha.3":"2019-03-30T21:01:08.188Z","1.0.0-beta.6":"2019-03-30T21:08:12.729Z","1.0.0-alpha.13":"2019-04-11T01:27:46.846Z","1.0.0-canary.14":"2019-04-11T02:05:39.641Z","1.0.0-canary.8":"2019-04-12T05:35:52.778Z","1.0.0-canary.9":"2019-04-12T09:02:02.395Z","1.0.0-beta.7":"2019-04-12T10:09:28.472Z","1.0.0-canary.0":"2019-04-14T20:58:33.365Z","1.0.0-canary.4":"2019-04-21T13:08:39.805Z","1.0.0-beta.8":"2019-04-24T23:19:47.235Z","1.0.0-canary.15":"2019-07-04T06:26:06.544Z","1.0.0-beta.9":"2019-07-04T07:32:06.804Z","1.0.0-canary.21":"2019-07-15T05:38:22.979Z","1.0.0-canary.26":"2019-09-16T20:29:35.481Z","1.0.0-canary.31":"2019-09-26T19:39:30.655Z","1.0.0-canary.36":"2019-09-29T08:58:29.292Z","1.0.0-canary.37":"2019-09-30T08:12:04.537Z","1.0.0-canary.38":"2019-10-04T10:27:33.839Z","1.0.0-canary.39":"2019-10-06T00:03:59.719Z","1.0.0-canary.40":"2019-10-06T01:16:27.616Z","1.0.0-canary.41":"2019-10-07T04:07:37.767Z","1.0.0-canary.47":"2019-10-22T06:02:12.233Z","1.0.0-canary.48":"2019-10-22T08:33:46.370Z","1.0.0-canary.52":"2019-10-24T21:57:00.274Z","1.0.0-beta.10":"2019-10-26T22:12:38.646Z","1.0.0-canary.3":"2019-10-27T02:22:04.903Z","1.0.0-dev.5":"2019-10-27T22:01:19.375Z","1.0.0-dev.6":"2019-10-29T05:10:32.701Z","1.0.0-dev.7":"2019-10-31T00:42:37.150Z","1.0.0-dev.8":"2019-11-01T06:39:08.645Z","1.0.0-dev.9":"2019-11-01T10:17:13.855Z","1.0.0-dev.10":"2019-11-02T23:29:36.338Z","1.0.0-dev.11":"2019-11-04T05:44:44.848Z","1.0.0-dev.12":"2019-11-04T07:44:12.672Z","1.0.0-dev.13":"2019-11-06T02:55:34.989Z","1.0.0-dev.14":"2019-11-06T22:35:00.502Z","1.0.0-dev.15":"2019-11-09T14:51:16.115Z","1.0.0-dev.16":"2019-11-09T16:47:28.931Z","1.0.0-dev.17":"2019-11-10T10:55:37.303Z","1.0.0-dev.18":"2019-11-10T22:17:23.424Z","1.0.0-dev.19":"2019-11-10T23:59:11.712Z","1.0.0-dev.20":"2019-11-11T00:11:54.982Z","1.0.0-dev.21":"2019-11-11T00:46:03.200Z","1.0.0-dev.22":"2019-11-11T01:49:43.181Z","1.0.0-dev.23":"2019-11-11T08:15:08.883Z","1.0.0-beta.11":"2019-11-12T04:25:46.772Z","1.0.0-dev.0":"2019-11-12T04:29:05.425Z","1.0.0-dev11.0":"2019-11-15T23:35:31.635Z","1.0.0-dev11.1":"2019-11-17T08:43:33.727Z","1.0.0-dev11.2":"2019-11-19T22:44:58.419Z","1.0.0-dev11.3":"2019-11-20T03:05:29.287Z","1.0.0-dev11.4":"2019-11-24T04:14:29.528Z","1.0.0-dev11.5":"2019-11-24T23:56:53.577Z","1.0.0-dev11.6":"2019-11-26T00:35:43.714Z","1.0.0-dev11.7":"2019-11-28T08:29:02.746Z","1.0.0-dev11.8":"2019-12-02T01:43:44.186Z","1.0.0-dev11.9":"2019-12-02T05:23:58.078Z","1.0.0-dev11.10":"2019-12-03T09:11:34.610Z","1.0.0-dev11.11":"2019-12-04T04:46:49.337Z","1.0.0-dev11.12":"2019-12-05T08:42:06.048Z","1.0.0-dev11.13":"2019-12-05T17:44:01.698Z","1.0.0-dev11.14":"2019-12-06T00:17:48.905Z","1.0.0-dev11.15":"2019-12-10T00:32:13.744Z","1.0.0-dev11.16":"2019-12-10T21:50:09.706Z","1.0.0-dev11.18":"2019-12-13T06:10:42.527Z","1.0.0-dev11.19":"2019-12-13T07:52:14.761Z","1.0.0-dev11.20":"2019-12-13T11:22:21.244Z","1.0.0-dev11.21":"2019-12-13T22:14:26.339Z","1.0.0-dev11.22":"2019-12-15T01:37:32.919Z","1.0.0-dev11.23":"2019-12-16T05:26:44.325Z","1.0.0-dev11.24":"2019-12-18T09:08:48.739Z","1.0.0-dev11.25":"2019-12-25T00:33:44.054Z","1.0.0-dev11.26":"2019-12-25T00:58:38.288Z","1.0.0-dev11.27":"2019-12-25T01:12:26.420Z","1.0.0-rc.0":"2019-12-25T01:55:47.208Z","1.0.0-rc0.0":"2019-12-25T03:14:39.649Z","1.0.0-rc0.1":"2019-12-25T03:49:48.817Z","1.0.0-rc0.2":"2019-12-31T00:48:41.412Z","1.0.0-rc0.3":"2019-12-31T01:42:54.514Z","1.0.0-rc0.4":"2020-01-01T06:17:46.469Z","1.0.0-rc0.5":"2020-01-02T01:45:13.298Z","1.0.0-rc0.6":"2020-01-02T01:57:03.267Z","1.0.0-rc0.7":"2020-01-05T02:52:44.489Z","1.0.0-rc0.8":"2020-01-08T08:16:03.702Z","1.0.0-rc0.9":"2020-01-09T03:40:40.815Z","1.0.0-rc0.10":"2020-01-12T08:35:47.088Z","1.0.0-rc0.11":"2020-01-12T08:48:15.696Z","1.0.0-rc0.12":"2020-01-14T21:04:34.151Z","1.0.0-rc0.13":"2020-01-15T22:59:18.539Z","1.0.0-rc0.14":"2020-01-15T23:31:34.868Z","1.0.0-rc0.15":"2020-01-17T22:12:22.265Z","1.0.0-rc0.16":"2020-01-23T03:20:17.251Z","1.0.0-rc.1":"2020-01-25T22:12:38.811Z","1.0.0-rc1.0":"2020-01-25T22:45:14.836Z","1.0.0-rc1.1":"2020-01-27T01:13:00.658Z","1.0.0-rc1.2":"2020-01-28T23:40:47.779Z","1.0.0-rc1.3":"2020-01-29T23:06:02.984Z","1.0.0-rc1.4":"2020-01-29T23:39:31.231Z","1.0.0-rc1.5":"2020-01-30T00:09:38.000Z","1.0.0-rc1.6":"2020-01-30T02:54:05.404Z","1.0.0-rc1.7":"2020-01-31T11:26:09.395Z","1.0.0-rc1.8":"2020-02-01T10:46:40.118Z","1.0.0-rc1.9":"2020-02-01T20:32:53.559Z","1.0.0-rc1.10":"2020-02-01T23:36:03.337Z","1.0.0-rc1.11":"2020-02-02T01:00:46.851Z","1.0.0-rc1.12":"2020-02-02T05:55:15.980Z","1.0.0-rc1.13":"2020-02-02T22:41:00.022Z","1.0.0-rc1.14":"2020-02-03T00:50:30.485Z","1.0.0-rc1.15":"2020-02-03T22:36:49.107Z","1.0.0-rc1.16":"2020-02-03T22:52:38.310Z","1.0.0-rc1.17":"2020-02-05T03:07:52.117Z","1.0.0-rc1.18":"2020-02-05T07:52:00.319Z","1.0.0-rc1.19":"2020-02-08T20:33:57.159Z","1.0.0-rc1.20":"2020-02-10T19:07:52.455Z","1.0.0-rc1.21":"2020-02-10T19:23:11.701Z","1.0.0-rc1.22":"2020-02-10T19:46:34.802Z","1.0.0-rc1.23":"2020-02-10T20:09:15.808Z","1.0.0-rc1.24":"2020-02-10T20:34:28.692Z","1.0.0-rc1.25":"2020-02-11T02:01:00.902Z","1.0.0-rc1.26":"2020-02-11T03:38:08.175Z","1.0.0-rc1.27":"2020-02-11T08:13:04.201Z","1.0.0-rc1.28":"2020-02-12T22:16:22.807Z","1.0.0-rc1.29":"2020-02-12T23:58:14.080Z","1.0.0-rc1.30":"2020-02-13T07:43:45.468Z","1.0.0-rc1.31":"2020-02-13T23:31:30.781Z","1.0.0-rc1.32":"2020-02-14T13:08:12.381Z","1.0.0-rc1.33":"2020-02-14T23:03:03.912Z","1.0.0-rc1.34":"2020-02-15T00:08:02.989Z","1.0.0-rc1.35":"2020-02-15T00:50:58.726Z","1.0.0-rc1.36":"2020-02-22T00:52:03.372Z","1.0.0-rc1.37":"2020-02-22T06:23:00.346Z","1.0.0-rc1.38":"2020-03-01T05:46:35.396Z","1.0.0-rc1.40":"2020-03-01T07:58:02.310Z","1.0.0-rc1.41":"2020-03-01T08:47:30.992Z","1.0.0-rc1.42":"2020-03-08T04:18:41.943Z","1.0.0-rc1.43":"2020-03-08T04:42:17.001Z","1.0.0-rc1.44":"2020-03-08T06:29:47.542Z","1.0.0-rc1.45":"2020-03-08T07:43:39.970Z","1.0.0-rc1.46":"2020-03-10T04:45:32.449Z","1.0.0-rc1.47":"2020-03-10T21:24:48.749Z","1.0.0-rc1.48":"2020-03-10T21:45:14.087Z","1.0.0-rc1.49":"2020-03-10T22:17:26.053Z","1.0.0-rc1.50":"2020-03-11T09:36:13.008Z","1.0.0-rc1.51":"2020-03-11T21:08:09.173Z","1.0.0-rc1.52":"2020-03-11T21:47:24.391Z","1.0.0-rc1.53":"2020-03-13T04:22:03.734Z","1.0.0-rc1.54":"2020-03-13T21:19:36.528Z","1.0.0-rc1.55":"2020-03-15T12:39:43.117Z","1.0.0-rc1.56":"2020-03-22T05:55:20.369Z","1.0.0-rc1.57":"2020-03-22T06:13:20.191Z","1.0.0-rc2.58":"2020-03-22T07:14:07.050Z","1.0.0-rc.2":"2020-03-22T07:18:35.761Z","1.0.0-rc2.59":"2020-03-22T07:18:58.639Z","1.0.0-rc2.0":"2020-03-22T07:21:16.742Z","1.0.0-canary1585436324.1":"2020-03-28T22:58:56.771Z","1.0.0-canary1587943544.2":"2020-04-26T23:25:57.959Z","1.0.0-canary1587968502.3":"2020-04-27T06:21:55.736Z","1.0.0-canary1587970552.4":"2020-04-27T06:56:04.722Z","1.0.0-canary1587980512.5":"2020-04-27T09:42:03.840Z","1.0.0-canary1587984926.6":"2020-04-27T10:55:38.275Z","1.0.0-canary1587988578.7":"2020-04-27T11:56:27.627Z","1.0.0-canary1588205336.8":"2020-04-30T00:09:07.968Z","1.0.0-canary1588541260.9":"2020-05-03T21:27:51.666Z","1.0.0-canary1588548530.10":"2020-05-03T23:29:06.159Z","1.0.0-canary1588575549.11":"2020-05-04T06:59:20.601Z","1.0.0-canary1588674233.12":"2020-05-05T10:24:04.573Z","1.0.0-canary1588915726.14":"2020-05-08T05:28:56.231Z","1.0.0-canary1588938574.15":"2020-05-08T11:49:46.662Z","1.0.0-canary1588940525.16":"2020-05-08T12:22:18.430Z","1.0.0-canary1588968898.17":"2020-05-08T20:15:10.179Z","1.0.0-canary1589001356.18":"2020-05-09T05:16:07.463Z","1.0.0-canary1589078917.19":"2020-05-10T02:48:51.712Z","1.0.0-canary1589094084.20":"2020-05-10T07:01:37.545Z","1.0.0-canary1589146005.21":"2020-05-10T21:26:59.010Z","1.0.0-canary1589230626.22":"2020-05-11T20:57:17.235Z","1.0.0-canary1589260536.23":"2020-05-12T05:15:49.301Z","1.0.0-canary1589282942.24":"2020-05-12T11:29:16.871Z","1.0.0-canary1589286463.25":"2020-05-12T12:27:53.822Z","1.0.0-canary1589432571.26":"2020-05-14T05:03:04.781Z","1.0.0-canary1589580811.27":"2020-05-15T22:13:42.869Z","1.0.0-canary1589604821.28":"2020-05-16T04:53:59.005Z","1.0.0-canary1589614058.29":"2020-05-16T07:27:49.089Z","1.0.0-canary1589675109.30":"2020-05-17T00:25:20.443Z","1.0.0-canary1589682003.31":"2020-05-17T02:20:15.526Z","1.0.0-canary1589712356.32":"2020-05-17T10:46:07.650Z","1.0.0-canary1589754316.33":"2020-05-17T22:25:27.961Z","1.0.0-canary1589788028.34":"2020-05-18T07:47:23.402Z","1.0.0-canary1589863748.35":"2020-05-19T04:49:21.256Z","1.0.0-canary1589938683.36":"2020-05-20T01:38:17.053Z","1.0.0-canary1589957613.37":"2020-05-20T06:53:45.490Z","1.0.0-canary1589959818.38":"2020-05-20T07:30:28.736Z","1.0.0-canary1590110442.39":"2020-05-22T01:20:53.142Z","1.0.0-canary1590187442.40":"2020-05-22T22:44:13.459Z","1.0.0-canary1590210208.41":"2020-05-23T05:03:39.654Z","1.0.0-canary1590571808.42":"2020-05-27T09:30:23.957Z","1.0.0-canary1590708371.43":"2020-05-28T23:26:24.064Z","1.0.0-canary1590713040.44":"2020-05-29T00:44:11.805Z","1.0.0-canary1590715866.45":"2020-05-29T01:31:18.796Z","1.0.0-canary1590834869.46":"2020-05-30T10:34:41.087Z","1.0.0-canary1591129335.47":"2020-06-02T20:22:34.060Z","1.0.0-canary1591130025.48":"2020-06-02T20:33:55.331Z","1.0.0-canary1591157350.49":"2020-06-03T04:09:22.075Z","1.0.0-canary1591175604.50":"2020-06-03T09:13:37.677Z","1.0.0-canary1591181937.51":"2020-06-03T10:59:12.313Z","1.0.0-canary1591242728.52":"2020-06-04T03:52:19.053Z","1.0.0-canary1591243227.53":"2020-06-04T04:00:39.575Z","1.0.0-canary1591264983.54":"2020-06-04T10:03:17.639Z","1.0.0-canary1591318908.55":"2020-06-05T01:02:01.886Z","1.0.0-canary1591319342.56":"2020-06-05T01:09:13.628Z","1.0.0-canary1591345663.57":"2020-06-05T08:27:56.106Z","1.0.0-canary1591350436.58":"2020-06-05T09:47:27.936Z","1.0.0-canary1591388762.59":"2020-06-05T20:26:15.729Z","1.0.0-canary1591390041.60":"2020-06-05T20:47:34.970Z","1.0.0-canary1591405562.62":"2020-06-06T01:06:19.636Z","1.0.0-canary1591595391.63":"2020-06-08T05:50:02.606Z","1.0.0-canary1591659885.64":"2020-06-08T23:44:56.948Z","1.0.0-canary1591675687.65":"2020-06-09T04:08:19.564Z","1.0.0-canary1591741423.66":"2020-06-09T22:23:54.614Z","1.0.0-canary1591786334.67":"2020-06-10T10:52:26.873Z","1.0.0-canary1591824688.68":"2020-06-10T21:31:48.073Z","1.0.0-canary1591849306.69":"2020-06-11T04:22:01.233Z","1.0.0-canary1591912793.70":"2020-06-11T22:00:06.796Z","1.0.0-canary1592172852.71":"2020-06-14T22:14:27.350Z","1.0.0-canary1592208003.72":"2020-06-15T08:00:21.279Z","1.0.0-canary1592211125.73":"2020-06-15T08:52:18.589Z","1.0.0-canary1592212488.74":"2020-06-15T09:14:59.214Z","1.0.0-canary1592225483.75":"2020-06-15T12:51:35.390Z","1.0.0-canary1592256667.76":"2020-06-15T21:31:19.875Z","1.0.0-canary1592271412.77":"2020-06-16T01:37:05.260Z","1.0.0-canary1592655926.78":"2020-06-20T12:25:38.907Z","1.0.0-canary1592806142.0":"2020-06-22T06:09:17.768Z","1.0.0-canary1592877058.0":"2020-06-23T01:51:19.984Z","1.0.0-canary1592886862.0":"2020-06-23T04:34:40.289Z","1.0.0-canary1593026645.0":"2020-06-24T19:24:23.298Z","1.0.0-canary1593069049.0":"2020-06-25T07:11:35.612Z","1.0.0-canary1593125083.0":"2020-06-25T22:44:59.824Z","1.0.0-canary.kbwv88am.0":"2020-06-26T23:47:54.792Z","1.0.0-canary.kbwxgwmn.0":"2020-06-27T00:50:38.237Z","1.0.0-canary.kbwy7dnv.0":"2020-06-27T01:11:14.408Z","1.0.0-canary.kbx8uhbt.0":"2020-06-27T06:09:06.486Z","1.0.0-canary.kby6baek.0":"2020-06-27T21:45:59.608Z","1.0.0-canary.kbyejgob.0":"2020-06-28T01:36:18.034Z","1.0.0-canary.kbyvfeer.0":"2020-06-28T09:29:03.896Z","1.0.0-canary.kc1bv7t9.0":"2020-06-30T02:44:45.171Z","1.0.0-canary.kc1j62zf.0":"2020-06-30T06:09:11.663Z","1.0.0-canary.kc1pwqwc.0":"2020-06-30T09:17:54.051Z","1.0.0-canary.kc6u1s8b.0":"2020-07-03T23:12:45.397Z","1.0.0-canary.kc6zqei2.0":"2020-07-04T01:51:41.846Z","1.0.0-canary.kc7ailpb.0":"2020-07-04T06:53:35.009Z","1.0.0-canary.kciolr74.0":"2020-07-12T06:13:23.374Z","1.0.0-canary.kckaf5hv.0":"2020-07-13T09:11:57.789Z","1.0.0-canary.kcras9pq.0":"2020-07-18T06:56:28.437Z","1.0.0-canary.kcrinrbo.0":"2020-07-18T10:36:55.993Z","1.0.0-canary.kcs5b8ny.0":"2020-07-18T21:11:01.003Z","1.0.0-canary.kcs8mlx5.0":"2020-07-18T22:43:50.439Z","1.0.0-canary.kctvam2m.0":"2020-07-20T02:06:08.812Z","1.0.0-canary.kcvksj6x.0":"2020-07-21T06:47:43.881Z","1.0.0-canary.kcwgd771.0":"2020-07-21T21:31:34.609Z","1.0.0-canary.kd1392pj.0":"2020-07-25T03:23:20.451Z","1.0.0-canary.kd13oe59.0":"2020-07-25T03:35:11.114Z","1.0.0-canary.kd18a18n.0":"2020-07-25T05:44:02.426Z","1.0.0-canary.kd1i13g6.0":"2020-07-25T10:17:01.055Z","1.0.0-canary.kd2p0nhi.0":"2020-07-26T06:20:23.730Z","1.0.0-canary.kd3mpv1u.0":"2020-07-26T22:03:44.998Z","1.0.0-canary.kd3rqvd4.0":"2020-07-27T00:24:31.302Z","1.0.0-canary.kd6wewq6.0":"2020-07-29T04:58:29.262Z","1.0.0-canary.kdf0lwtn.0":"2020-08-03T21:18:03.715Z","1.0.0-canary.kdgfdc3a.0":"2020-08-04T20:59:08.513Z","1.0.0-canary.kdi5mwkd.0":"2020-08-06T02:02:12.911Z","1.0.0-canary.kdi9bq15.0":"2020-08-06T03:45:26.905Z","1.0.0-canary.kdid6yqc.0":"2020-08-06T05:33:42.322Z","1.0.0-canary.kdjdfg7t.0":"2020-08-06T22:28:04.764Z","1.0.0-canary.kdjj7hrf.0":"2020-08-07T01:09:50.757Z","1.0.0-canary.kdkqxkys.0":"2020-08-07T21:33:51.622Z","1.0.0-canary.kdqpr7yp.0":"2020-08-12T01:47:30.062Z","1.0.0-canary.kdtej428.0":"2020-08-13T22:56:34.598Z","1.0.0-canary.kdtibqoo.0":"2020-08-14T00:42:49.708Z","1.0.0-canary.kdvw2za0.0":"2020-08-15T16:43:31.302Z","1.0.0-canary.ke8zqwdv.0":"2020-08-24T20:47:10.827Z","1.0.0-canary.ke91wrl1.0":"2020-08-24T21:47:55.461Z","1.0.0-canary.ke93q61k.0":"2020-08-24T22:38:29.018Z","1.0.0-canary.keao8m4o.0":"2020-08-26T01:00:28.961Z","1.0.0-canary.keaththw.0":"2020-08-26T03:27:44.518Z","1.0.0-canary.kebz3sj5.0":"2020-08-26T22:52:26.377Z","1.0.0-canary.kedxkqhh.0":"2020-08-28T07:45:11.844Z","1.0.0-canary.keeqwum9.0":"2020-08-28T21:26:32.690Z","1.0.0-canary.keexw8ts.0":"2020-08-29T00:41:52.895Z","1.0.0-canary.kef2e3lg.0":"2020-08-29T02:47:43.685Z","1.0.0-canary.kef5312a.0":"2020-08-29T04:03:06.210Z","1.0.0-canary.kefh7qik.0":"2020-08-29T09:42:41.884Z","1.0.0-canary.kejh0j48.0":"2020-09-01T04:48:08.393Z","1.0.0-canary.kel7fsyz.0":"2020-09-02T09:55:38.133Z","1.0.0-canary.kela2rem.0":"2020-09-02T11:09:30.633Z","1.0.0-canary.kenou8nl.0":"2020-09-04T03:38:18.866Z","1.0.0-canary.keoswy6l.0":"2020-09-04T22:20:08.092Z","1.0.0-canary.kepjwxp9.0":"2020-09-05T10:55:56.588Z","1.0.0-canary.keqn8ee3.0":"2020-09-06T05:16:36.436Z","1.0.0-canary.kerx6yap.0":"2020-09-07T02:43:12.006Z","1.0.0-canary.key2i6eu.0":"2020-09-11T09:58:38.027Z","1.0.0-canary.keyz518l.0":"2020-09-12T01:12:07.821Z","1.0.0-canary.kezbpkd4.0":"2020-09-12T07:03:57.880Z","1.0.0-canary.kezj7wpq.0":"2020-09-12T10:34:13.384Z","1.0.0-canary.kf1ngfd8.0":"2020-09-13T22:08:22.774Z","1.0.0-canary.kf32nfdw.0":"2020-09-14T22:01:28.616Z","1.0.0-canary.kf3gt31v.0":"2020-09-15T04:37:46.490Z","1.0.0-canary.kf6gpx8m.0":"2020-09-17T06:58:41.768Z","1.0.0-canary.kf6lwncy.0":"2020-09-17T09:23:50.279Z","1.0.0-canary.kf6ox8zq.0":"2020-09-17T10:48:15.605Z","1.0.0-canary.kf83flsl.0":"2020-09-18T10:22:18.093Z","1.0.0-canary.kf8yuiqx.0":"2020-09-19T01:01:37.060Z","1.0.0-canary.kf9he5u8.0":"2020-09-19T09:40:47.244Z","1.0.0-canary.kfdk5pvu.0":"2020-09-22T06:09:15.730Z","1.0.0-canary.kfg7x8ie.0":"2020-09-24T02:50:02.528Z","1.0.0-canary.kfku9xyl.0":"2020-09-27T08:26:57.928Z","1.0.0-canary.kflwpa3o.0":"2020-09-28T02:22:37.254Z","1.0.0-canary.kfm9pqez.0":"2020-09-28T08:26:49.174Z","1.0.0-canary.kfnbyb01.0":"2020-09-29T02:17:15.088Z","1.0.0-canary.kfndaw7c.0":"2020-09-29T02:55:06.937Z","1.0.0-canary.kfok0ubn.0":"2020-09-29T22:50:56.136Z","1.0.0-canary.kg3h9ub9.0":"2020-10-10T09:30:29.335Z","1.0.0-canary.kg3j85u4.0":"2020-10-10T10:25:11.213Z","1.0.0-canary.kg45m6uw.0":"2020-10-10T20:51:59.432Z","1.0.0-canary.kg4vj92q.0":"2020-10-11T08:57:29.293Z","1.0.0-canary.kg68uhyf.0":"2020-10-12T07:57:58.989Z","1.0.0-canary.kg6g8g5u.0":"2020-10-12T11:24:56.567Z","1.0.0-canary.kg736xd2.0":"2020-10-12T22:07:24.094Z","1.0.0-canary.kg8ugq0v.0":"2020-10-14T03:38:44.010Z","1.0.0-canary.kg9x6kqr.0":"2020-10-14T21:42:28.143Z","1.0.0-canary.kgbtqzo0.0":"2020-10-16T05:41:54.970Z","1.0.0-canary.kgc219pd.0":"2020-10-16T09:33:54.107Z","1.0.0-canary.kgd3ozpo.0":"2020-10-17T03:08:06.602Z","1.0.0-canary.kgdlm368.0":"2020-10-17T11:29:40.745Z","1.0.0-canary.kgdmgzlm.0":"2020-10-17T11:53:41.983Z","1.0.0-canary.kgea9t96.0":"2020-10-17T23:00:00.647Z","1.0.0-canary.kgelgslo.0":"2020-10-18T04:13:22.401Z","1.0.0-canary.kgevjcoc.0":"2020-10-18T08:55:17.117Z","1.0.0-canary.kgg7oarz.0":"2020-10-19T07:22:49.492Z","1.0.0-canary.kgm10qxk.0":"2020-10-23T09:03:12.165Z","1.0.0-canary.kgm5iq6j.0":"2020-10-23T11:09:12.201Z","1.0.0-canary.kgrcn168.0":"2020-10-27T02:27:25.174Z","1.0.0-canary.kgsjdgwx.0":"2020-10-27T22:23:42.273Z","1.0.0-canary.kgsps60c.0":"2020-10-28T01:22:59.772Z","1.0.0-canary.kgxa6f2k.0":"2020-10-31T06:05:05.096Z","1.0.0-canary.kgxnbu9k.0":"2020-10-31T12:13:11.432Z","1.0.0-canary.kgy8ctsk.0":"2020-10-31T22:01:43.921Z","1.0.0-canary.kgyal5xs.0":"2020-10-31T23:04:13.198Z","1.0.0-canary.kgydir3d.0":"2020-11-01T00:26:20.961Z","1.0.0-canary.kgzo7o56.0":"2020-11-01T22:13:27.721Z","1.0.0-canary.kgzuclth.0":"2020-11-02T01:05:11.521Z","1.0.0-canary.kgzwhr7g.0":"2020-11-02T02:05:10.629Z","1.0.0-canary.kh1h0fly.0":"2020-11-03T04:27:23.115Z","1.0.0-canary.kh1tdkjc.0":"2020-11-03T10:13:32.971Z","1.0.0-canary.kh1ut1x4.0":"2020-11-03T10:53:33.971Z","1.0.0-canary.kh2ho8dd.0":"2020-11-03T21:33:37.875Z","1.0.0-canary.kh2j3gf1.0":"2020-11-03T22:13:30.633Z","1.0.0-canary.kh2jnpxg.0":"2020-11-03T22:29:13.471Z","1.0.0-canary.kh2k6f0d.0":"2020-11-03T22:43:44.050Z","1.0.0-canary.kh2lwwtd.0":"2020-11-03T23:32:23.318Z","1.0.0-canary.kh2p7j3o.0":"2020-11-04T01:04:35.206Z","1.0.0-canary.kh4ew4ru.0":"2020-11-05T05:51:26.221Z","1.0.0-canary.kh5ddq10.0":"2020-11-05T21:56:59.197Z","1.0.0-canary.kh5h50n0.0":"2020-11-05T23:42:00.369Z","1.0.0-canary.kh5jla6k.0":"2020-11-06T00:50:39.142Z","1.0.0-canary.kh5lyii8.0":"2020-11-06T01:56:54.412Z","1.0.0-canary.kh61az31.0":"2020-11-06T09:06:39.543Z","1.0.0-canary.khbdcr5h.0":"2020-11-10T02:42:44.520Z","1.0.0-canary.khchhyyz.0":"2020-11-10T21:26:28.432Z","1.0.0-canary.khdxsl4k.0":"2020-11-11T21:50:23.185Z","1.0.0-canary.khelfoev.0":"2020-11-12T08:52:11.149Z","1.0.0-canary.khg09zf1.0":"2020-11-13T08:35:30.692Z","1.0.0-canary.khiras28.0":"2020-11-15T06:47:25.278Z","1.0.0-canary.khy4xbmr.0":"2020-11-26T01:05:26.395Z","1.0.0-canary.khye3mjx.0":"2020-11-26T05:22:16.897Z","1.0.0-canary.ki2idjwy.0":"2020-11-29T02:33:01.069Z","1.0.0-canary.ki34emgi.778":"2020-11-29T12:49:42.987Z","1.0.0-canary.ki3i90va.0":"2020-11-29T19:17:16.228Z","1.0.0-canary.ki3imu3t.0":"2020-11-29T19:28:01.230Z","1.0.0-canary.ki3w8w7z.0":"2020-11-30T01:49:05.021Z","1.0.0-canary.ki3x3sog.0":"2020-11-30T02:13:07.635Z","1.0.0-canary.ki6vxtqr.0":"2020-12-02T04:03:48.627Z","1.0.0-canary.ki7l9jjj.0":"2020-12-02T15:52:53.221Z","1.0.0-canary.kibdskyc.0":"2020-12-05T07:34:40.646Z","1.0.0-canary.kicmb1c7.0":"2020-12-06T04:20:45.234Z","1.0.0-canary.kijl4du6.0":"2020-12-11T01:22:02.008Z","1.0.0-canary.kijvk681.0":"2020-12-11T06:14:19.316Z","1.0.0-canary.kik3wda7.0":"2020-12-11T10:07:46.314Z","1.0.0-canary.kikr9t32.0":"2020-12-11T21:02:09.884Z","1.0.0-canary.kikrxvdo.0":"2020-12-11T21:20:37.314Z","1.0.0-rc.3":"2020-12-11T22:38:35.436Z","1.0.0-canary.kikuveya.0":"2020-12-11T22:42:41.484Z","1.0.0-canary.kil5u21m.0":"2020-12-12T03:49:32.020Z","1.0.0-canary.kilfkajr.0":"2020-12-12T08:21:54.968Z","1.0.0-canary.kio0koex.0":"2020-12-14T03:45:35.760Z","1.0.0-canary.kio5kror.0":"2020-12-14T06:05:45.668Z","1.0.0-canary.kiql6xtk.0":"2020-12-15T22:58:24.362Z","1.0.0-canary.kiqtyzoi.0":"2020-12-16T03:04:06.862Z","1.0.0-canary.kiqybylo.0":"2020-12-16T05:06:12.119Z","1.0.0-canary.kiqz68ge.0":"2020-12-16T05:29:45.713Z","1.0.0-canary.kiutiy7w.0":"2020-12-18T22:02:41.340Z","1.0.0-canary.kiux0iyr.0":"2020-12-18T23:40:21.476Z","1.0.0-canary.kiybwviv.0":"2020-12-21T09:00:43.124Z","1.0.0-canary.kjaisf7u.0":"2020-12-29T21:46:28.806Z","1.0.0-canary.kjjm44kp.0":"2021-01-05T06:29:29.159Z","1.0.0-canary.kjkhwbz4.0":"2021-01-05T21:19:16.287Z","1.0.0-canary.kjkji9nf.0":"2021-01-05T22:04:16.850Z","1.0.0-rc.4":"2021-01-07T20:41:00.102Z","1.0.0-canary.kjnbgxhp.0":"2021-01-07T20:42:36.695Z","1.0.0-canary.kjnbl337.0":"2021-01-07T20:45:47.199Z","1.0.0-canary.kjnmrhgp.0":"2021-01-08T01:58:45.466Z","1.0.0-canary.kjvyett6.0":"2021-01-13T21:46:56.755Z","1.0.0-canary.kjxsazqq.0":"2021-01-15T04:31:31.653Z","1.0.0-canary.kk11hnon.0":"2021-01-17T11:11:58.223Z","1.0.0-canary.kk38o98m.0":"2021-01-19T00:08:37.960Z","1.0.0-rc.5":"2021-01-23T22:51:23.283Z","1.0.0-canary.kkab9bkf.0":"2021-01-23T22:55:19.751Z","1.0.0-canary.kkadsgg7.0":"2021-01-24T00:06:12.559Z","1.0.0-canary.kkc8txq0.0":"2021-01-25T07:22:55.339Z","1.0.0-canary.kkdoevg4.0":"2021-01-26T07:27:02.412Z","1.0.0-canary.kkdoitvt.0":"2021-01-26T07:29:57.629Z","1.0.0-canary.kkdt09l2.0":"2021-01-26T09:35:43.593Z","1.0.0-canary.kkdt2hvw.0":"2021-01-26T09:37:42.242Z","1.0.0-canary.kkejdvjl.0":"2021-01-26T21:53:56.739Z","1.0.0-canary.kkg5b59d.0":"2021-01-28T00:55:32.597Z","1.0.0-canary.kkgqkazf.0":"2021-01-28T10:50:28.483Z","1.0.0-canary.kkkat4et.0":"2021-01-30T22:40:28.171Z","1.0.0-canary.kkm3mgeg.0":"2021-02-01T04:54:52.507Z","1.0.0-canary.kkq0pyxn.0":"2021-02-03T22:44:43.940Z","1.0.0-canary.kkqennf6.0":"2021-02-04T05:14:54.914Z","1.0.0-canary.kkrk2bux.0":"2021-02-05T00:33:58.540Z","1.0.0-canary.kks1yrhn.0":"2021-02-05T08:55:05.973Z","1.0.0-canary.kks8erbp.0":"2021-02-05T11:55:27.503Z","1.0.0-canary.kku388np.0":"2021-02-06T19:05:57.559Z","1.0.0-canary.kkuufn5p.0":"2021-02-07T07:47:40.012Z","1.0.0-canary.kkuvw4g4.0":"2021-02-07T08:28:23.447Z","1.0.0-canary.kkvui0xl.0":"2021-02-08T00:37:15.249Z","1.0.0-canary.kkymfdyo.0":"2021-02-09T23:14:32.019Z","1.0.0-rc.6":"2021-02-09T23:15:53.903Z","1.0.0-canary.kkymmnlf.0":"2021-02-09T23:20:16.025Z","1.0.0-canary.kkymq36h.0":"2021-02-09T23:22:53.329Z","1.0.0-canary.kkzy8nhn.0":"2021-02-10T21:32:58.952Z","1.0.0-canary.kl05lf66.0":"2021-02-11T00:58:48.334Z","1.0.0-canary.kl07x6lm.0":"2021-02-11T02:04:02.011Z","1.0.0-canary.kl0bv9c3.0":"2021-02-11T03:54:49.110Z","1.0.0-canary.kl0bxs5i.0":"2021-02-11T03:56:26.424Z","1.0.0-canary.kl1fczf8.0":"2021-02-11T22:20:03.895Z","1.0.0-canary.kl1gvy67.0":"2021-02-11T23:02:44.344Z","1.0.0-canary.kl1h5unf.0":"2021-02-11T23:10:25.219Z","1.0.0-canary.kl1inky0.0":"2021-02-11T23:52:12.498Z","1.0.0-canary.kl1j3znx.0":"2021-02-12T00:04:57.216Z","1.0.0-canary.kl4e1a3i.0":"2021-02-14T00:06:09.405Z","1.0.0-rc.7":"2021-02-14T00:30:53.451Z","1.0.0-canary.kl4f3kpu.0":"2021-02-14T00:35:57.814Z","1.0.0-canary.kl4fa8dq.0":"2021-02-14T00:41:09.261Z","1.0.0-canary.kl7kgjgn.0":"2021-02-16T05:29:18.211Z","1.0.0-canary.kl7kqymo.0":"2021-02-16T05:37:25.595Z","1.0.0-canary.kl7ksxtq.0":"2021-02-16T05:38:58.019Z","1.0.0-canary.kl7l7kcb.0":"2021-02-16T05:50:23.140Z","1.0.0-canary.kl7lb6y7.0":"2021-02-16T05:53:08.869Z","1.0.0-canary.kl8sdtfq.0":"2021-02-17T01:59:01.973Z","1.0.0-canary.kl8stpwu.0":"2021-02-17T02:11:17.066Z","1.0.0-canary.kl8t06nx.0":"2021-02-17T02:16:34.255Z","1.0.0-canary.kl9xn7u9.0":"2021-02-17T21:13:58.599Z","1.0.0-canary.kl9ymvhu.0":"2021-02-17T21:41:44.050Z","1.0.0-canary.kla2b92n.0":"2021-02-17T23:24:41.499Z","1.0.0-canary.kla9tiov.0":"2021-02-18T02:54:47.678Z","1.0.0-canary.klajfgyt.0":"2021-02-18T07:23:49.285Z","1.0.0-canary.klbfry2t.0":"2021-02-18T22:29:18.944Z","1.0.0-canary.klbk83lx.0":"2021-02-19T00:33:54.258Z","1.0.0-canary.klfp4p97.0":"2021-02-21T22:02:16.967Z","1.0.0-canary.klfrodyc.0":"2021-02-21T23:13:31.729Z","1.0.0-canary.klfunam6.0":"2021-02-22T00:36:41.022Z","1.0.0-canary.klhat76f.0":"2021-02-23T00:56:54.664Z","1.0.0-canary.klhhyfma.0":"2021-02-23T04:16:56.480Z","1.0.0-canary.klht2f03.0":"2021-02-23T09:28:01.323Z","1.0.0-rc.8":"2021-02-23T09:58:12.206Z","1.0.0-canary.klhu5pq0.0":"2021-02-23T09:59:05.949Z","1.0.0-canary.klhuaxso.0":"2021-02-23T10:02:36.189Z","1.0.0-canary.klijm2be.0":"2021-02-23T21:51:09.641Z","1.0.0-canary.klkqupsw.0":"2021-02-25T10:49:24.897Z","1.0.0-canary.klkrhc86.0":"2021-02-25T11:07:04.926Z","1.0.0-canary.kllawr22.0":"2021-02-25T20:10:49.194Z","1.0.0-canary.klljk0nt.0":"2021-02-26T00:12:54.405Z","1.0.0-canary.klmi2vb6.0":"2021-02-26T16:19:25.112Z","1.0.0-canary.km12ir1x.0":"2021-03-08T21:00:29.781Z","1.0.0-canary.km2nk4a0.0":"2021-03-09T23:37:18.008Z","1.0.0-canary.km2rs2yr.0":"2021-03-10T01:35:18.508Z","1.0.0-canary.km2s8d9l.0":"2021-03-10T01:47:52.521Z","1.0.0-canary.km2vnjhs.0":"2021-03-10T03:23:44.639Z","1.0.0-canary.km367x9q.0":"2021-03-10T08:19:29.135Z","1.0.0-canary.km6t3qv9.0":"2021-03-12T21:23:27.230Z","1.0.0-canary.km7h2c2v.0":"2021-03-13T08:34:07.626Z","1.0.0-canary.km7n022p.0":"2021-03-13T11:20:20.997Z","1.0.0-canary.km8aye8n.0":"2021-03-13T22:30:52.245Z","1.0.0-canary.km8s1w44.0":"2021-03-14T06:29:27.006Z","1.0.0-rc.9":"2021-03-14T06:30:40.254Z","1.0.0-canary.km8s5ws4.0":"2021-03-14T06:32:34.175Z","1.0.0-canary.km8s9uyx.0":"2021-03-14T06:35:39.110Z","1.0.0-canary.km8sfd2k.0":"2021-03-14T06:39:54.465Z","1.0.0-canary.km8w78rt.0":"2021-03-14T08:25:35.582Z","1.0.0-canary.km928moe.0":"2021-03-14T11:14:41.849Z","1.0.0-canary.kmbarc7y.0":"2021-03-16T00:48:52.015Z","1.0.0-canary.kmbk3p6o.0":"2021-03-16T05:10:18.335Z","1.0.0-canary.kmc224qy.0":"2021-03-16T13:33:31.631Z","1.0.0-canary.kmcgxz7a.0":"2021-03-16T20:29:36.043Z","1.0.0-canary.kmcw0m2i.0":"2021-03-17T03:31:32.191Z","1.0.0-canary.kmcwi1b3.0":"2021-03-17T03:45:05.628Z","1.0.0-canary.kmdwmjgv.0":"2021-03-17T20:36:26.780Z","1.0.0-canary.kmhht7ke.0":"2021-03-20T08:52:42.912Z","1.0.0-canary.kmicd1v9.0":"2021-03-20T23:07:57.267Z","1.0.0-canary.kmie38bd.0":"2021-03-20T23:56:19.911Z","1.0.0-canary.kmik3vju.0":"2021-03-21T02:44:47.901Z","1.0.0-canary.kmjs5vc1.0":"2021-03-21T23:18:03.342Z","1.0.0-canary.kmjsmuc2.0":"2021-03-21T23:31:17.728Z","1.0.0-canary.kmjvtv0f.0":"2021-03-22T01:00:49.070Z","1.0.0-canary.kmjxaqhv.0":"2021-03-22T01:41:58.820Z","1.0.0-canary.kmk0sp78.0":"2021-03-22T03:19:49.186Z","1.0.0-canary.kmk2n8qb.0":"2021-03-22T04:11:35.677Z","1.0.0-canary.kmk7q7m0.0":"2021-03-22T06:33:51.377Z","1.0.0-canary.kmn96zl1.0":"2021-03-24T09:38:11.669Z","1.0.0-canary.kmq5uqhq.0":"2021-03-26T10:27:57.421Z","1.0.0-canary.kmqwm9wn.0":"2021-03-26T22:57:09.171Z","1.0.0-canary.kmr00yo9.0":"2021-03-27T00:32:32.842Z","1.0.0-canary.kmsfdxhp.0":"2021-03-28T00:30:19.613Z","1.0.0-canary.kmwl4pkf.0":"2021-03-30T22:22:10.977Z","1.0.0-canary.kmwqkr4d.0":"2021-03-31T00:54:37.003Z","1.0.0-canary.kmwrmm9a.0":"2021-03-31T01:24:12.551Z","1.0.0-canary.kn0utu8p.0":"2021-04-02T22:04:48.493Z","1.0.0-canary.kn0vwk7u.0":"2021-04-02T22:34:49.438Z","1.0.0-canary.kn0wxny3.0":"2021-04-02T23:03:40.306Z","1.0.0-canary.kn0zmjcf.0":"2021-04-03T00:19:01.938Z","1.0.0-canary.kn0zpd8a.0":"2021-04-03T00:21:12.560Z","1.0.0-rc.10":"2021-04-03T00:26:10.401Z","1.0.0-canary.kn1022qg.0":"2021-04-03T00:31:15.704Z","1.0.0-canary.kn2ad3ga.0":"2021-04-03T22:07:25.190Z","1.0.0-canary.kn2m7vyl.0":"2021-04-04T03:39:17.262Z","1.0.0-canary.kn2n12i2.0":"2021-04-04T04:02:00.550Z","1.0.0-canary.kn3nvmmu.0":"2021-04-04T21:13:27.582Z","1.0.0-canary.kn4iqgwx.0":"2021-04-05T11:37:19.496Z","1.0.0-canary.kn6jg2m9.0":"2021-04-06T21:32:47.689Z","1.0.0-canary.kn6nkglc.0":"2021-04-06T23:28:04.368Z","1.0.0-canary.kn6o0llk.0":"2021-04-06T23:40:47.586Z","1.0.0-canary.knc8f5oa.0":"2021-04-10T21:10:38.641Z","1.0.0-canary.kndte9ap.0":"2021-04-11T23:45:35.760Z","1.0.0-canary.knee3yzp.0":"2021-04-12T09:25:35.315Z","1.0.0-canary.knf85b5d.0":"2021-04-12T23:26:19.158Z","1.0.0-canary.knf9jw7j.0":"2021-04-13T00:05:48.550Z","1.0.0-canary.knh8sx6p.0":"2021-04-14T09:20:13.585Z","1.0.0-canary.knhwgudr.0":"2021-04-14T20:22:41.509Z","1.0.0-canary.knhx180i.0":"2021-04-14T20:38:45.495Z","1.0.0-canary.knhxsgj2.0":"2021-04-14T20:59:44.113Z","1.0.0-rc.11":"2021-04-14T21:05:45.065Z","1.0.0-canary.knhy71n1.0":"2021-04-14T21:11:05.090Z","1.0.0-canary.knhyqnl5.0":"2021-04-14T21:26:22.365Z","1.0.0-canary.knkt86ec.0":"2021-04-16T21:15:19.150Z","1.0.0-canary.knkv8u23.0":"2021-04-16T22:11:49.412Z","1.0.0-canary.knp0yxc3.0":"2021-04-19T20:03:11.009Z","1.0.0-canary.knp1j82w.0":"2021-04-19T20:18:55.088Z","1.0.0-canary.knp3gee9.0":"2021-04-19T21:12:43.749Z","1.0.0-canary.knp3sfsk.0":"2021-04-19T21:22:03.989Z","1.0.0-canary.knp41y12.0":"2021-04-19T21:29:30.251Z","1.0.0-canary.knp4em2q.0":"2021-04-19T21:39:21.629Z","1.0.0-canary.knp4tvtv.0":"2021-04-19T21:51:15.167Z","1.0.0-canary.knpqbnp3.0":"2021-04-20T07:52:53.853Z","1.0.0-canary.knpqlai2.0":"2021-04-20T08:00:26.626Z","1.0.0-canary.knpqugsy.0":"2021-04-20T08:07:36.563Z","1.0.0-canary.knpr3yrc.0":"2021-04-20T08:14:56.793Z","1.0.0-canary.knpredha.0":"2021-04-20T08:23:11.289Z","1.0.0-canary.knqst0tw.0":"2021-04-21T01:50:13.981Z","1.0.0-canary.knwbehg3.0":"2021-04-24T22:29:35.025Z","1.0.0-canary.knwe89mn.0":"2021-04-24T23:48:40.860Z","1.0.0-canary.knwelcuw.0":"2021-04-24T23:58:51.093Z","1.0.0-rc.12":"2021-04-25T00:48:00.393Z","1.0.0-canary.knwgj07p.0":"2021-04-25T00:53:02.479Z","1.0.0-canary.knwigz60.0":"2021-04-25T01:47:29.023Z","1.0.0-canary.knxxq9sl.0":"2021-04-26T01:42:24.379Z","1.0.0-canary.kny3g8ud.0":"2021-04-26T04:22:29.636Z","1.0.0-canary.ko0m5xjm.0":"2021-04-27T22:41:53.827Z","1.0.0-canary.ko0njduj.0":"2021-04-27T23:20:33.740Z","1.0.0-canary.ko9izbbk.0":"2021-05-04T04:22:48.347Z","1.0.0":"2021-05-04T08:56:49.375Z","1.0.1-canary.ko9sx9va.0":"2021-05-04T09:01:04.651Z","1.0.1-canary.kogs41ax.0":"2021-05-09T06:12:46.925Z","1.0.1-canary.0":"2021-05-09T07:49:29.543Z","1.0.1-canary.korp8h.0":"2021-05-16T21:37:39.010Z","1.0.1-canary.korwha.0":"2021-05-17T01:00:29.799Z","1.0.1":"2021-05-18T00:29:05.437Z","1.0.2-canary.kotc3q.0":"2021-05-18T01:05:36.322Z","1.0.2-canary.koxlv5.0":"2021-05-21T00:50:00.117Z","1.0.2-canary.koxomt.0":"2021-05-21T02:07:25.962Z","1.0.2-canary.koxqgm.0":"2021-05-21T02:58:38.052Z","1.0.2-canary.koxrd3.0":"2021-05-21T03:23:52.468Z","1.0.2-canary.koxsli.0":"2021-05-21T03:58:25.507Z","1.0.2-canary.koxtci.0":"2021-05-21T04:19:22.764Z","1.0.2-canary.koxuq1.0":"2021-05-21T04:58:15.725Z","1.0.2-canary.koxvj6.0":"2021-05-21T05:20:35.895Z","1.0.2-canary.koxwne.0":"2021-05-21T05:51:54.023Z","1.0.2-canary.koxx1j.0":"2021-05-21T06:02:55.857Z","1.0.2-canary.kp2a4t.0":"2021-05-24T07:20:23.481Z","1.0.1-canary.22":"2021-05-24T07:39:20.501Z","1.0.1-canary.26":"2021-05-25T10:09:12.235Z","1.0.1-canary.27":"2021-05-28T01:04:50.535Z","1.0.1-canary.36":"2021-06-04T01:27:50.268Z","1.0.1-canary.40":"2021-06-06T05:33:52.736Z","1.0.2":"2021-06-06T20:20:59.067Z","1.0.3-canary.3":"2021-06-10T12:55:57.650Z","1.0.3-canary.6":"2021-06-12T22:28:05.967Z","1.0.3-canary.7":"2021-06-14T21:12:17.471Z","1.0.3-canary.9":"2021-06-14T22:52:54.821Z","1.0.3-canary.10":"2021-06-17T01:58:21.856Z","1.0.3-canary.13":"2021-06-23T06:03:28.496Z","1.0.3-canary.16":"2021-06-28T06:28:02.103Z","1.0.3":"2021-06-28T08:07:53.933Z","1.0.4-canary.1":"2021-07-01T22:51:37.551Z","1.0.4-canary.2":"2021-07-02T05:12:36.885Z","1.0.4-canary.5":"2021-07-03T08:12:46.758Z","1.0.4":"2021-07-05T17:52:25.394Z","1.0.5-canary.1":"2021-07-09T00:41:14.538Z","1.0.5-canary.3":"2021-07-23T00:40:17.861Z","1.0.5":"2021-07-25T04:32:06.200Z","1.0.6-canary.3":"2021-09-03T00:39:12.855Z","1.0.6-canary.5":"2021-09-10T00:45:08.103Z","1.0.6":"2021-10-04T04:56:22.706Z","1.0.7-canary.0":"2021-10-08T00:45:22.326Z","1.0.7-canary.4":"2022-12-07T03:06:36.679Z","1.0.7-canary.5":"2022-12-07T09:46:16.015Z","1.1.1-canary.0":"2022-12-28T00:15:25.513Z","1.1.1-canary.1":"2022-12-28T11:02:41.057Z","1.1.1":"2023-01-03T04:18:33.390Z","1.1.2-canary.1":"2023-01-07T04:33:26.808Z","1.1.2-canary.2":"2023-01-12T06:24:21.910Z","1.1.2-canary.4":"2023-01-15T22:11:09.336Z","1.1.2":"2023-01-20T00:17:10.280Z","1.1.3-canary.2":"2023-01-26T23:24:41.649Z","1.1.3-canary.3":"2023-01-27T21:30:30.418Z","1.1.3":"2023-03-10T02:34:24.194Z"},"maintainers":[{"name":"ktutnik","email":"ktutnik@gmail.com"}],"description":"Delightful Node.js Rest Framework","keywords":["MVC","Koa","Web API","JSON Service","TDD"],"author":{"name":"Ketut Sandiarsa"},"license":"MIT","readme":"# Plumier\nDelightful Node.js Rest Framework\n\n[![Build Status](https://github.com/plumier/plumier/workflows/ubuntu/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Aubuntu)\n[![Build status](https://github.com/plumier/plumier/workflows/windows/badge.svg)](https://github.com/plumier/plumier/actions?query=workflow%3Awindows)\n[![Coverage Status](https://coveralls.io/repos/github/plumier/plumier/badge.svg?branch=master)](https://coveralls.io/github/plumier/plumier?branch=master)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)\n[![npm](https://img.shields.io/npm/v/plumier/canary)](https://www.npmjs.com/package/plumier?activeTab=versions)\n[![npm](https://img.shields.io/npm/v/plumier/latest)](https://www.npmjs.com/package/plumier?activeTab=versions)\n\n## Documentation \nRead the project documentation on https://plumierjs.com\n\n## Contributing\nTo run Plumier project on local machine, some setup/app required\n\n### App requirements\n* Visual Studio Code (Recommended)\n* Yarn (required)\n\n### Local Setup\n* Fork and clone the project `git clone` \n* Install dependencies by `yarn install`\n* Run test by `yarn test`\n\n### Debugging\nPlumier already provided vscode `task` and `launch` setting. To start debugging a test scenario:\n* Locate the test file and narrow the test runs by using `.only`\n* Put breakpoint on any location you need on `.ts` file \n* On start/debug configuration select `Jest Current File` and start debugging\n","readmeFilename":"readme.md","homepage":"https://plumierjs.com","bugs":{"url":"https://github.com/plumier/plumier/issues"},"repository":{"url":"git+https://github.com/plumier/plumier.git"}}